Merge pull request #469 from lounres/feature/polynomials
Feature: Polynomials and rational functions
This commit is contained in:
commit
99fee476bc
22
README.md
22
README.md
@ -214,6 +214,28 @@ One can still use generic algebras though.
|
||||
>
|
||||
> **Maturity**: EXPERIMENTAL
|
||||
|
||||
### [kmath-polynomial](kmath-polynomial)
|
||||
>
|
||||
>
|
||||
> **Maturity**: PROTOTYPE
|
||||
>
|
||||
> **Features:**
|
||||
> - [polynomial abstraction](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt) : Abstraction for polynomial spaces.
|
||||
> - [rational function abstraction](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt) : Abstraction for rational functions spaces.
|
||||
> - ["list" polynomials](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt) : List implementation of univariate polynomials.
|
||||
> - ["list" rational functions](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt) : List implementation of univariate rational functions.
|
||||
> - ["list" polynomials and rational functions constructors](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/listConstructors.kt) : Constructors for list polynomials and rational functions.
|
||||
> - ["list" polynomials and rational functions utilities](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt) : Utilities for list polynomials and rational functions.
|
||||
> - ["numbered" polynomials](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/NumberedRationalFunction.kt) : Numbered implementation of multivariate polynomials.
|
||||
> - ["numbered" rational functions](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt) : Numbered implementation of multivariate rational functions.
|
||||
> - ["numbered" polynomials and rational functions constructors](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt) : Constructors for numbered polynomials and rational functions.
|
||||
> - ["numbered" polynomials and rational functions utilities](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt) : Utilities for numbered polynomials and rational functions.
|
||||
> - ["labeled" polynomials](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt) : Labeled implementation of multivariate polynomials.
|
||||
> - ["labeled" rational functions](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt) : Labeled implementation of multivariate rational functions.
|
||||
> - ["labeled" polynomials and rational functions constructors](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt) : Constructors for labeled polynomials and rational functions.
|
||||
> - ["labeled" polynomials and rational functions utilities](kmath-polynomial/src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt) : Utilities for labeled polynomials and rational functions.
|
||||
|
||||
|
||||
### [kmath-stat](kmath-stat)
|
||||
>
|
||||
>
|
||||
|
172
docs/polynomials.md
Normal file
172
docs/polynomials.md
Normal file
@ -0,0 +1,172 @@
|
||||
# Polynomials and Rational Functions
|
||||
|
||||
KMath provides a way to work with uni- and multivariate polynomials and rational functions. It includes full support of arithmetic operations of integers, **constants** (elements of ring polynomials are build over), variables (for certain multivariate implementations), polynomials and rational functions encapsulated in so-called **polynomial space** and **rational function space** and some other utilities such as algebraic differentiation and substitution.
|
||||
|
||||
## Concrete realizations
|
||||
|
||||
There are 3 approaches to represent polynomials:
|
||||
1. For univariate polynomials one can represent and store polynomial as a list of coefficients for each power of the variable. I.e. polynomial $a_0 + \dots + a_n x^n $ can be represented as a finite sequence $(a_0; \dots; a_n)$. (Compare to sequential definition of polynomials.)
|
||||
2. For multivariate polynomials one can represent and store polynomial as a matching (in programming it is called "map" or "dictionary", in math it is called [functional relation](https://en.wikipedia.org/wiki/Binary_relation#Special_types_of_binary_relations)) of each "**term signature**" (that describes what variables and in what powers appear in the term) with corresponding coefficient of the term. But there are 2 possible approaches of term signature representation:
|
||||
1. One can number all the variables, so term signature can be represented as a sequence describing powers of the variables. I.e. signature of term $c \\; x_0^{d_0} \dots x_n^{d_n} $ (for natural or zero $d_i $) can be represented as a finite sequence $(d_0; \dots; d_n)$.
|
||||
2. One can represent variables as objects ("**labels**"), so term signature can be also represented as a matching of each appeared variable with its power in the term. I.e. signature of term $c \\; x_0^{d_0} \dots x_n^{d_n} $ (for natural non-zero $d_i $) can be represented as a finite matching $(x_0 \to d_1; \dots; x_n \to d_n)$.
|
||||
|
||||
All that three approaches are implemented by "list", "numbered", and "labeled" versions of polynomials and polynomial spaces respectively. Whereas all rational functions are represented as fractions with corresponding polynomial numerator and denominator, and rational functions' spaces are implemented in the same way as usual field of rational numbers (or more precisely, as any field of fractions over integral domain) should be implemented.
|
||||
|
||||
So here are a bit of details. Let `C` by type of constants. Then:
|
||||
1. `ListPolynomial`, `ListPolynomialSpace`, `ListRationalFunction` and `ListRationalFunctionSpace` implement the first scenario. `ListPolynomial` stores polynomial $a_0 + \dots + a_n x^n $ as a coefficients list `listOf(a_0, ..., a_n)` (of type `List<C>`).
|
||||
|
||||
They also have variation `ScalableListPolynomialSpace` that replaces former polynomials and implements `ScaleOperations`.
|
||||
2. `NumberedPolynomial`, `NumberedPolynomialSpace`, `NumberedRationalFunction` and `NumberedRationalFunctionSpace` implement second scenario. `NumberedPolynomial` stores polynomials as structures of type `Map<List<UInt>, C>`. Signatures are stored as `List<UInt>`. To prevent ambiguity signatures should not end with zeros.
|
||||
3. `LabeledPolynomial`, `LabeledPolynomialSpace`, `LabeledRationalFunction` and `LabeledRationalFunctionSpace` implement third scenario using common `Symbol` as variable type. `LabeledPolynomial` stores polynomials as structures of type `Map<Map<Symbol, UInt>, C>`. Signatures are stored as `Map<Symbol, UInt>`. To prevent ambiguity each signature should not map any variable to zero.
|
||||
|
||||
### Example: `ListPolynomial`
|
||||
|
||||
For example, polynomial $2 - 3x + x^2 $ (with `Int` coefficients) is represented
|
||||
```kotlin
|
||||
val polynomial: ListPolynomial<Int> = ListPolynomial(listOf(2, -3, 1))
|
||||
// or
|
||||
val polynomial: ListPolynomial<Int> = ListPolynomial(2, -3, 1)
|
||||
```
|
||||
|
||||
All algebraic operations can be used in corresponding space:
|
||||
```kotlin
|
||||
val computationResult = Int.algebra.listPolynomialSpace {
|
||||
ListPolynomial(2, -3, 1) + ListPolynomial(0, 6) == ListPolynomial(2, 3, 1)
|
||||
}
|
||||
|
||||
println(computationResult) // true
|
||||
```
|
||||
|
||||
For more see [examples](../examples/src/main/kotlin/space/kscience/kmath/functions/polynomials.kt).
|
||||
|
||||
### Example: `NumberedPolynomial`
|
||||
|
||||
For example, polynomial $3 + 5 x_1 - 7 x_0^2 x_2 $ (with `Int` coefficients) is represented
|
||||
```kotlin
|
||||
val polynomial: NumberedPolynomial<Int> = NumberedPolynomial(
|
||||
mapOf(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
)
|
||||
)
|
||||
// or
|
||||
val polynomial: NumberedPolynomial<Int> = NumberedPolynomial(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
)
|
||||
```
|
||||
|
||||
All algebraic operations can be used in corresponding space:
|
||||
```kotlin
|
||||
val computationResult = Int.algebra.numberedPolynomialSpace {
|
||||
NumberedPolynomial(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
) + NumberedPolynomial(
|
||||
listOf(0u, 1u) to -5,
|
||||
listOf(0u, 0u, 0u, 4u) to 4,
|
||||
) == NumberedPolynomial(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 0,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
listOf(0u, 0u, 0u, 4u) to 4,
|
||||
)
|
||||
}
|
||||
|
||||
println(computationResult) // true
|
||||
```
|
||||
|
||||
For more see [examples](../examples/src/main/kotlin/space/kscience/kmath/functions/polynomials.kt).
|
||||
|
||||
### Example: `LabeledPolynomial`
|
||||
|
||||
For example, polynomial $3 + 5 y - 7 x^2 z $ (with `Int` coefficients) is represented
|
||||
```kotlin
|
||||
val polynomial: LabeledPolynomial<Int> = LabeledPolynomial(
|
||||
mapOf(
|
||||
mapOf<Symbol, UInt>() to 3,
|
||||
mapOf(y to 1u) to 5,
|
||||
mapOf(x to 2u, z to 1u) to -7,
|
||||
)
|
||||
)
|
||||
// or
|
||||
val polynomial: LabeledPolynomial<Int> = LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 3,
|
||||
mapOf(y to 1u) to 5,
|
||||
mapOf(x to 2u, z to 1u) to -7,
|
||||
)
|
||||
```
|
||||
|
||||
All algebraic operations can be used in corresponding space:
|
||||
```kotlin
|
||||
val computationResult = Int.algebra.labeledPolynomialSpace {
|
||||
LabeledPolynomial(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
) + LabeledPolynomial(
|
||||
listOf(0u, 1u) to -5,
|
||||
listOf(0u, 0u, 0u, 4u) to 4,
|
||||
) == LabeledPolynomial(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 0,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
listOf(0u, 0u, 0u, 4u) to 4,
|
||||
)
|
||||
}
|
||||
|
||||
println(computationResult) // true
|
||||
```
|
||||
|
||||
For more see [examples](../examples/src/main/kotlin/space/kscience/kmath/functions/polynomials.kt).
|
||||
|
||||
## Abstract entities (interfaces and abstract classes)
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
Polynomial <|-- ListPolynomial
|
||||
Polynomial <|-- NumberedPolynomial
|
||||
Polynomial <|-- LabeledPolynomial
|
||||
|
||||
RationalFunction <|-- ListRationalFunction
|
||||
RationalFunction <|-- NumberedRationalFunction
|
||||
RationalFunction <|-- LabeledRationalFunction
|
||||
|
||||
Ring <|-- PolynomialSpace
|
||||
PolynomialSpace <|-- MultivariatePolynomialSpace
|
||||
PolynomialSpace <|-- PolynomialSpaceOverRing
|
||||
|
||||
Ring <|-- RationalFunctionSpace
|
||||
RationalFunctionSpace <|-- MultivariateRationalFunctionSpace
|
||||
RationalFunctionSpace <|-- RationalFunctionSpaceOverRing
|
||||
RationalFunctionSpace <|-- RationalFunctionSpaceOverPolynomialSpace
|
||||
RationalFunctionSpace <|-- PolynomialSpaceOfFractions
|
||||
RationalFunctionSpaceOverPolynomialSpace <|-- MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpace
|
||||
MultivariateRationalFunctionSpace <|-- MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpace
|
||||
MultivariateRationalFunctionSpace <|-- MultivariatePolynomialSpaceOfFractions
|
||||
PolynomialSpaceOfFractions <|-- MultivariatePolynomialSpaceOfFractions
|
||||
```
|
||||
|
||||
There are implemented `Polynomial` and `RationalFunction` interfaces as abstractions of polynomials and rational functions respectively (although, there is not a lot of logic in them) and `PolynomialSpace` and `RationalFunctionSpace` (that implement `Ring` interface) as abstractions of polynomials' and rational functions' spaces respectively. More precisely, that means they allow to declare common logic of interaction with such objects and spaces:
|
||||
- `Polynomial` does not provide any logic. It is marker interface.
|
||||
- `RationalFunction` provides numerator and denominator of rational function and destructuring declaration for them.
|
||||
- `PolynomialSpace` provides all possible arithmetic interactions of integers, constants (of type `C`), and polynomials (of type `P`) like addition, subtraction, multiplication, and some others and common properties like degree of polynomial.
|
||||
- `RationalFunctionSpace` provides the same as `PolynomialSpace` but also for rational functions: all possible arithmetic interactions of integers, constants (of type `C`), polynomials (of type `P`), and rational functions (of type `R`) like addition, subtraction, multiplication, division (in some cases), and some others and common properties like degree of polynomial.
|
||||
|
||||
Then to add abstraction of similar behaviour with variables (in multivariate case) there are implemented `MultivariatePolynomialSpace` and `MultivariateRationalFunctionSpace`. They just include variables (of type `V`) in the interactions of the entities.
|
||||
|
||||
Also, to remove boilerplates there were provided helping subinterfaces and abstract subclasses:
|
||||
- `PolynomialSpaceOverRing` allows to replace implementation of interactions of integers and constants with implementations from provided ring over constants (of type `A: Ring<C>`).
|
||||
- `RationalFunctionSpaceOverRing` — the same but for `RationalFunctionSpace`.
|
||||
- `RationalFunctionSpaceOverPolynomialSpace` — the same but "the inheritance" includes interactions with polynomials from provided `PolynomialSpace`.
|
||||
- `PolynomialSpaceOfFractions` is actually abstract subclass of `RationalFunctionSpace` that implements all fractions boilerplates with provided (`protected`) constructor of rational functions by polynomial numerator and denominator.
|
||||
- `MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpace` and `MultivariatePolynomialSpaceOfFractions` — the same stories of operators inheritance and fractions boilerplates respectively but in multivariate case.
|
||||
|
||||
## Utilities
|
||||
|
||||
For all kinds of polynomials there are provided (implementation details depend on kind of polynomials) such common utilities as:
|
||||
1. differentiation and anti-differentiation,
|
||||
2. substitution, invocation and functional representation.
|
@ -15,6 +15,8 @@ dependencies {
|
||||
implementation(project(":kmath-coroutines"))
|
||||
implementation(project(":kmath-commons"))
|
||||
implementation(project(":kmath-complex"))
|
||||
implementation(project(":kmath-functions"))
|
||||
implementation(project(":kmath-polynomial"))
|
||||
implementation(project(":kmath-optimization"))
|
||||
implementation(project(":kmath-stat"))
|
||||
implementation(project(":kmath-viktor"))
|
||||
|
@ -0,0 +1,399 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("LocalVariableName")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.expressions.symbol
|
||||
import space.kscience.kmath.operations.algebra
|
||||
import space.kscience.kmath.operations.invoke
|
||||
|
||||
|
||||
/**
|
||||
* Shows [ListPolynomial]s' and [ListRationalFunction]s' capabilities.
|
||||
*/
|
||||
fun listPolynomialsExample() {
|
||||
// [ListPolynomial] is a representation of a univariate polynomial as a list of coefficients from the least term to
|
||||
// the greatest term. For example,
|
||||
val polynomial1: ListPolynomial<Int> = ListPolynomial(listOf(2, -3, 1))
|
||||
// represents polynomial 2 + (-3) x + x^2
|
||||
|
||||
// There are also shortcut fabrics:
|
||||
val polynomial2: ListPolynomial<Int> = ListPolynomial(2, -3, 1)
|
||||
println(polynomial1 == polynomial2) // true
|
||||
// and even
|
||||
val polynomial3: ListPolynomial<Int> = 57.asListPolynomial()
|
||||
val polynomial4: ListPolynomial<Int> = ListPolynomial(listOf(57))
|
||||
println(polynomial3 == polynomial4) // true
|
||||
|
||||
val polynomial5: ListPolynomial<Int> = ListPolynomial(3, -1)
|
||||
// For every ring there can be provided a polynomial ring:
|
||||
Int.algebra.listPolynomialSpace {
|
||||
println(-polynomial5 == ListPolynomial(-3, 1)) // true
|
||||
println(polynomial1 + polynomial5 == ListPolynomial(5, -4, 1)) // true
|
||||
println(polynomial1 - polynomial5 == ListPolynomial(-1, -2, 1)) // true
|
||||
println(polynomial1 * polynomial5 == ListPolynomial(6, -11, 6, -1)) // true
|
||||
}
|
||||
// You can even write
|
||||
val x: ListPolynomial<Double> = ListPolynomial(0.0, 1.0)
|
||||
val polynomial6: ListPolynomial<Double> = ListPolynomial(2.0, -3.0, 1.0)
|
||||
Double.algebra.listPolynomialSpace {
|
||||
println(2 - 3 * x + x * x == polynomial6)
|
||||
println(2.0 - 3.0 * x + x * x == polynomial6)
|
||||
}
|
||||
|
||||
// Also there are some utilities for polynomials:
|
||||
println(polynomial1.substitute(Int.algebra, 1) == 0) // true, because 2 + (-3) * 1 + 1^2 = 0
|
||||
println(polynomial1.substitute(Int.algebra, polynomial5) == polynomial1) // true, because 2 + (-3) * (3-x) + (3-x)^2 = 2 - 3x + x^2
|
||||
println(polynomial1.derivative(Int.algebra) == ListPolynomial(-3, 2)) // true, (2 - 3x + x^2)' = -3 + 2x
|
||||
println(polynomial1.nthDerivative(Int.algebra, 2) == 2.asListPolynomial()) // true, (2 - 3x + x^2)'' = 2
|
||||
|
||||
// Lastly, there are rational functions and some other utilities:
|
||||
Double.algebra.listRationalFunctionSpace {
|
||||
val rationalFunction1: ListRationalFunction<Double> = ListRationalFunction(listOf(2.0, -3.0, 1.0), listOf(3.0, -1.0))
|
||||
// It's just (2 - 3x + x^2)/(3 - x)
|
||||
|
||||
val rationalFunction2 : ListRationalFunction<Double> = ListRationalFunction(listOf(5.0, -4.0, 1.0), listOf(3.0, -1.0))
|
||||
// It's just (5 - 4x + x^2)/(3 - x)
|
||||
|
||||
println(rationalFunction1 + 1 == rationalFunction2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows [NumberedPolynomial]s' and [NumberedRationalFunction]s' capabilities.
|
||||
*/
|
||||
fun numberedPolynomialsExample() {
|
||||
// Consider polynomial
|
||||
// 3 + 5 x_2 - 7 x_1^2 x_3
|
||||
// Consider, for example, its term -7 x_1^2 x_3. -7 is a coefficient of the term, whereas (2, 0, 1, 0, 0, ...) is
|
||||
// description of degrees of variables x_1, x_2, ... in the term. Such description with removed leading zeros
|
||||
// [2, 0, 1] is called "signature" of the term -7 x_1^2 x_3.
|
||||
|
||||
val polynomial1: NumberedPolynomial<Int>
|
||||
with(Int.algebra) {
|
||||
// [NumberedPolynomial] is a representation of a multivariate polynomial, that stores terms in a map with terms'
|
||||
// signatures as the map's keys and terms' coefficients as corresponding values. For example,
|
||||
polynomial1 = NumberedPolynomial(
|
||||
mapOf(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
)
|
||||
)
|
||||
// represents polynomial 3 + 5 x_2 - 7 x_1^2 x_3
|
||||
|
||||
// This `NumberedPolynomial` function needs context of either ring of constant (as `Int.algebra` in this example)
|
||||
// or space of NumberedPolynomials over it. To understand why it is like this see documentations of functions
|
||||
// NumberedPolynomial and NumberedPolynomialWithoutCheck
|
||||
|
||||
// There are also shortcut fabrics:
|
||||
val polynomial2: NumberedPolynomial<Int> = NumberedPolynomial(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
)
|
||||
println(polynomial1 == polynomial2) // true
|
||||
// and even
|
||||
val polynomial3: NumberedPolynomial<Int> = 57.asNumberedPolynomial() // This one actually does not algebraic context!
|
||||
val polynomial4: NumberedPolynomial<Int> = NumberedPolynomial(listOf<UInt>() to 57)
|
||||
println(polynomial3 == polynomial4) // true
|
||||
|
||||
numberedPolynomialSpace {
|
||||
// Also there is DSL for constructing NumberedPolynomials:
|
||||
val polynomial5: NumberedPolynomial<Int> = NumberedPolynomialDSL1 {
|
||||
3 {}
|
||||
5 { 1 inPowerOf 1u }
|
||||
-7 with { 0 pow 2u; 2 pow 1u }
|
||||
// `pow` and `inPowerOf` are the same
|
||||
// `with` is omittable
|
||||
}
|
||||
println(polynomial1 == polynomial5) // true
|
||||
|
||||
// Unfortunately the DSL does not work good in bare context of constants' ring, so for now it's disabled and
|
||||
// works only in NumberedPolynomialSpace and NumberedRationalFunctionSpace
|
||||
}
|
||||
}
|
||||
|
||||
val polynomial6: NumberedPolynomial<Int> = Int.algebra {
|
||||
NumberedPolynomial(
|
||||
listOf<UInt>() to 7,
|
||||
listOf(0u, 1u) to -5,
|
||||
listOf(2u, 0u, 1u) to 0,
|
||||
listOf(0u, 0u, 0u, 4u) to 4,
|
||||
)
|
||||
}
|
||||
// For every ring there can be provided a polynomial ring:
|
||||
Int.algebra.numberedPolynomialSpace {
|
||||
println(
|
||||
-polynomial6 == NumberedPolynomial(
|
||||
listOf<UInt>() to -7,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to 0,
|
||||
listOf(0u, 0u, 0u, 4u) to (-4),
|
||||
)
|
||||
) // true
|
||||
println(
|
||||
polynomial1 + polynomial6 == NumberedPolynomial(
|
||||
listOf<UInt>() to 10,
|
||||
listOf(0u, 1u) to 0,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
listOf(0u, 0u, 0u, 4u) to 4,
|
||||
)
|
||||
) // true
|
||||
println(
|
||||
polynomial1 - polynomial6 == NumberedPolynomial(
|
||||
listOf<UInt>() to -4,
|
||||
listOf(0u, 1u) to 10,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
listOf(0u, 0u, 0u, 4u) to -4,
|
||||
)
|
||||
) // true
|
||||
|
||||
polynomial1 * polynomial6 // Multiplication works too
|
||||
}
|
||||
|
||||
Double.algebra.numberedPolynomialSpace {
|
||||
// You can even write
|
||||
val x_1: NumberedPolynomial<Double> = NumberedPolynomial(listOf(1u) to 1.0)
|
||||
val x_2: NumberedPolynomial<Double> = NumberedPolynomial(listOf(0u, 1u) to 1.0)
|
||||
val x_3: NumberedPolynomial<Double> = NumberedPolynomial(listOf(0u, 0u, 1u) to 1.0)
|
||||
val polynomial7: NumberedPolynomial<Double> = NumberedPolynomial(
|
||||
listOf<UInt>() to 3.0,
|
||||
listOf(0u, 1u) to 5.0,
|
||||
listOf(2u, 0u, 1u) to -7.0,
|
||||
)
|
||||
Double.algebra.listPolynomialSpace {
|
||||
println(3 + 5 * x_2 - 7 * x_1 * x_1 * x_3 == polynomial7)
|
||||
println(3.0 + 5.0 * x_2 - 7.0 * x_1 * x_1 * x_3 == polynomial7)
|
||||
}
|
||||
}
|
||||
|
||||
Int.algebra.numberedPolynomialSpace {
|
||||
val x_4: NumberedPolynomial<Int> = NumberedPolynomial(listOf(0u, 0u, 0u, 4u) to 1)
|
||||
// Also there are some utilities for polynomials:
|
||||
println(polynomial1.substitute(mapOf(0 to 1, 1 to -2, 2 to -1)) == 0.asNumberedPolynomial()) // true,
|
||||
// because it's substitution x_1 -> 1, x_2 -> -2, x_3 -> -1,
|
||||
// so 3 + 5 x_2 - 7 x_1^2 x_3 = 3 + 5 * (-2) - 7 * 1^2 * (-1) = 3 - 10 + 7 = 0
|
||||
println(
|
||||
polynomial1.substitute(mapOf(1 to x_4)) == NumberedPolynomial(
|
||||
listOf<UInt>() to 3,
|
||||
listOf(0u, 1u) to 5,
|
||||
listOf(2u, 0u, 1u) to -7,
|
||||
)
|
||||
) // true, because it's substitution x_2 -> x_4, so result is 3 + 5 x_4 - 7 x_1^2 x_3
|
||||
println(
|
||||
polynomial1.derivativeWithRespectTo(Int.algebra, 1) ==
|
||||
NumberedPolynomial(listOf<UInt>() to 5)
|
||||
) // true, d/dx_2 (3 + 5 x_2 - 7 x_1^2 x_3) = 5
|
||||
}
|
||||
|
||||
// Lastly, there are rational functions and some other utilities:
|
||||
Double.algebra.numberedRationalFunctionSpace {
|
||||
val rationalFunction1: NumberedRationalFunction<Double> = NumberedRationalFunction(
|
||||
NumberedPolynomial(
|
||||
listOf<UInt>() to 2.0,
|
||||
listOf(1u) to -3.0,
|
||||
listOf(2u) to 1.0,
|
||||
),
|
||||
NumberedPolynomial(
|
||||
listOf<UInt>() to 3.0,
|
||||
listOf(1u) to -1.0,
|
||||
)
|
||||
)
|
||||
// It's just (2 - 3x + x^2)/(3 - x) where x = x_1
|
||||
|
||||
val rationalFunction2: NumberedRationalFunction<Double> = NumberedRationalFunction(
|
||||
NumberedPolynomial(
|
||||
listOf<UInt>() to 5.0,
|
||||
listOf(1u) to -4.0,
|
||||
listOf(2u) to 1.0,
|
||||
),
|
||||
NumberedPolynomial(
|
||||
listOf<UInt>() to 3.0,
|
||||
listOf(1u) to -1.0,
|
||||
)
|
||||
)
|
||||
// It's just (5 - 4x + x^2)/(3 - x) where x = x_1
|
||||
|
||||
println(rationalFunction1 + 1 == rationalFunction2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows [LabeledPolynomial]s' and [LabeledRationalFunction]s' capabilities.
|
||||
*/
|
||||
fun labeledPolynomialsExample() {
|
||||
val x by symbol
|
||||
val y by symbol
|
||||
val z by symbol
|
||||
val t by symbol
|
||||
|
||||
// Consider polynomial
|
||||
// 3 + 5 y - 7 x^2 z
|
||||
// Consider, for example, its term -7 x^2 z. -7 is a coefficient of the term, whereas matching (x -> 2, z -> 3) is
|
||||
// description of degrees of variables x_1, x_2, ... in the term. Such description is called "signature" of the
|
||||
// term -7 x_1^2 x_3.
|
||||
|
||||
val polynomial1: LabeledPolynomial<Int>
|
||||
with(Int.algebra) {
|
||||
// [LabeledPolynomial] is a representation of a multivariate polynomial, that stores terms in a map with terms'
|
||||
// signatures as the map's keys and terms' coefficients as corresponding values. For example,
|
||||
polynomial1 = LabeledPolynomial(
|
||||
mapOf(
|
||||
mapOf<Symbol, UInt>() to 3,
|
||||
mapOf(y to 1u) to 5,
|
||||
mapOf(x to 2u, z to 1u) to -7,
|
||||
)
|
||||
)
|
||||
// represents polynomial 3 + 5 y - 7 x^2 z
|
||||
|
||||
// This `LabeledPolynomial` function needs context of either ring of constant (as `Int.algebra` in this example)
|
||||
// or space of LabeledPolynomials over it. To understand why it is like this see documentations of functions
|
||||
// LabeledPolynomial and LabeledPolynomialWithoutCheck
|
||||
|
||||
// There are also shortcut fabrics:
|
||||
val polynomial2: LabeledPolynomial<Int> = LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 3,
|
||||
mapOf(y to 1u) to 5,
|
||||
mapOf(x to 2u, z to 1u) to -7,
|
||||
)
|
||||
println(polynomial1 == polynomial2) // true
|
||||
// and even
|
||||
val polynomial3: LabeledPolynomial<Int> = 57.asLabeledPolynomial() // This one actually does not algebraic context!
|
||||
val polynomial4: LabeledPolynomial<Int> = LabeledPolynomial(mapOf<Symbol, UInt>() to 57)
|
||||
println(polynomial3 == polynomial4) // true
|
||||
|
||||
labeledPolynomialSpace {
|
||||
// Also there is DSL for constructing NumberedPolynomials:
|
||||
val polynomial5: LabeledPolynomial<Int> = LabeledPolynomialDSL1 {
|
||||
3 {}
|
||||
5 { y inPowerOf 1u }
|
||||
-7 with { x pow 2u; z pow 1u }
|
||||
// `pow` and `inPowerOf` are the same
|
||||
// `with` is omittable
|
||||
}
|
||||
println(polynomial1 == polynomial5) // true
|
||||
|
||||
// Unfortunately the DSL does not work good in bare context of constants' ring, so for now it's disabled and
|
||||
// works only in NumberedPolynomialSpace and NumberedRationalFunctionSpace
|
||||
}
|
||||
}
|
||||
|
||||
val polynomial6: LabeledPolynomial<Int> = Int.algebra {
|
||||
LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 7,
|
||||
mapOf(y to 1u) to -5,
|
||||
mapOf(x to 2u, z to 1u) to 0,
|
||||
mapOf(t to 4u) to 4,
|
||||
)
|
||||
}
|
||||
// For every ring there can be provided a polynomial ring:
|
||||
Int.algebra.labeledPolynomialSpace {
|
||||
println(
|
||||
-polynomial6 == LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to -7,
|
||||
mapOf(y to 1u) to 5,
|
||||
mapOf(x to 2u, z to 1u) to 0,
|
||||
mapOf(t to 4u) to -4,
|
||||
)
|
||||
) // true
|
||||
println(
|
||||
polynomial1 + polynomial6 == LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 10,
|
||||
mapOf(y to 1u) to 0,
|
||||
mapOf(x to 2u, z to 1u) to -7,
|
||||
mapOf(t to 4u) to 4,
|
||||
)
|
||||
) // true
|
||||
println(
|
||||
polynomial1 - polynomial6 == LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to -4,
|
||||
mapOf(y to 1u) to 10,
|
||||
mapOf(x to 2u, z to 1u) to -7,
|
||||
mapOf(t to 4u) to -4,
|
||||
)
|
||||
) // true
|
||||
|
||||
polynomial1 * polynomial6 // Multiplication works too
|
||||
}
|
||||
|
||||
Double.algebra.labeledPolynomialSpace {
|
||||
// You can even write
|
||||
val polynomial7: LabeledPolynomial<Double> = LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 3.0,
|
||||
mapOf(y to 1u) to 5.0,
|
||||
mapOf(x to 2u, z to 1u) to -7.0,
|
||||
)
|
||||
Double.algebra.listPolynomialSpace {
|
||||
println(3 + 5 * y - 7 * x * x * z == polynomial7)
|
||||
println(3.0 + 5.0 * y - 7.0 * x * x * z == polynomial7)
|
||||
}
|
||||
}
|
||||
|
||||
Int.algebra.labeledPolynomialSpace {
|
||||
// Also there are some utilities for polynomials:
|
||||
println(polynomial1.substitute(mapOf(x to 1, y to -2, z to -1)) == 0.asLabeledPolynomial()) // true,
|
||||
// because it's substitution x -> 1, y -> -2, z -> -1,
|
||||
// so 3 + 5 y - 7 x^2 z = 3 + 5 * (-2) - 7 * 1^2 * (-1) = 3 - 10 + 7 = 0
|
||||
println(
|
||||
polynomial1.substitute(mapOf(y to t.asPolynomial())) == LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 3,
|
||||
mapOf(t to 1u) to 5,
|
||||
mapOf(x to 2u, z to 1u) to -7,
|
||||
)
|
||||
) // true, because it's substitution y -> t, so result is 3 + 5 t - 7 x^2 z
|
||||
println(
|
||||
polynomial1.derivativeWithRespectTo(Int.algebra, y) == LabeledPolynomial(mapOf<Symbol, UInt>() to 5)
|
||||
) // true, d/dy (3 + 5 y - 7 x^2 z) = 5
|
||||
}
|
||||
|
||||
// Lastly, there are rational functions and some other utilities:
|
||||
Double.algebra.labeledRationalFunctionSpace {
|
||||
val rationalFunction1: LabeledRationalFunction<Double> = LabeledRationalFunction(
|
||||
LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 2.0,
|
||||
mapOf(x to 1u) to -3.0,
|
||||
mapOf(x to 2u) to 1.0,
|
||||
),
|
||||
LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 3.0,
|
||||
mapOf(x to 1u) to -1.0,
|
||||
)
|
||||
)
|
||||
// It's just (2 - 3x + x^2)/(3 - x)
|
||||
|
||||
val rationalFunction2: LabeledRationalFunction<Double> = LabeledRationalFunction(
|
||||
LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 5.0,
|
||||
mapOf(x to 1u) to -4.0,
|
||||
mapOf(x to 2u) to 1.0,
|
||||
),
|
||||
LabeledPolynomial(
|
||||
mapOf<Symbol, UInt>() to 3.0,
|
||||
mapOf(x to 1u) to -1.0,
|
||||
)
|
||||
)
|
||||
// It's just (5 - 4x + x^2)/(3 - x)
|
||||
|
||||
println(rationalFunction1 + 1 == rationalFunction2)
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
println("ListPolynomials:")
|
||||
listPolynomialsExample()
|
||||
println()
|
||||
|
||||
println("NumberedPolynomials:")
|
||||
numberedPolynomialsExample()
|
||||
println()
|
||||
|
||||
println("ListPolynomials:")
|
||||
labeledPolynomialsExample()
|
||||
println()
|
||||
}
|
@ -99,6 +99,15 @@ public inline fun <T, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R)
|
||||
return accumulator
|
||||
}
|
||||
|
||||
/**
|
||||
* Fold given buffer according to indexed [operation]
|
||||
*/
|
||||
public inline fun <T : Any, R> Buffer<T>.foldIndexed(initial: R, operation: (index: Int, acc: R, T) -> R): R {
|
||||
var accumulator = initial
|
||||
for (index in this.indices) accumulator = operation(index, accumulator, get(index))
|
||||
return accumulator
|
||||
}
|
||||
|
||||
/**
|
||||
* Zip two buffers using given [transform].
|
||||
*/
|
||||
|
@ -6,12 +6,18 @@ plugins {
|
||||
|
||||
description = "Functions, integration and interpolation"
|
||||
|
||||
kotlin.sourceSets.commonMain {
|
||||
dependencies {
|
||||
api(project(":kmath-core"))
|
||||
kotlin.sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(project(":kmath-core"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dokkaPlugin("org.jetbrains.dokka:mathjax-plugin:${npmlibs.versions.dokka.get()}")
|
||||
}
|
||||
|
||||
readme {
|
||||
maturity = ru.mipt.npm.gradle.Maturity.EXPERIMENTAL
|
||||
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
|
||||
|
@ -3,130 +3,278 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress", "PARAMETER_NAME_CHANGED_ON_OVERRIDE")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.*
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.operations.ScaleOperations
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.math.max
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
/**
|
||||
* Polynomial coefficients model without fixation on specific context they are applied to.
|
||||
* Represents univariate polynomial that stores its coefficients in a [List].
|
||||
*
|
||||
* @param coefficients constant is the leftmost coefficient.
|
||||
* @param C the type of constants.
|
||||
*/
|
||||
public class Polynomial<out T>(public val coefficients: List<T>) {
|
||||
public data class Polynomial<out C>(
|
||||
/**
|
||||
* List that contains coefficients of the polynomial.
|
||||
*
|
||||
* Every monomial \(a x^d\) is stored as a coefficient \(a\) placed
|
||||
* into the list at index \(d\). For example, coefficients of a polynomial \(5 x^2 - 6\) can be represented as
|
||||
* ```
|
||||
* listOf(
|
||||
* -6, // -6 +
|
||||
* 0, // 0 x +
|
||||
* 5, // 5 x^2
|
||||
* )
|
||||
* ```
|
||||
* and also as
|
||||
* ```
|
||||
* listOf(
|
||||
* -6, // -6 +
|
||||
* 0, // 0 x +
|
||||
* 5, // 5 x^2
|
||||
* 0, // 0 x^3
|
||||
* 0, // 0 x^4
|
||||
* )
|
||||
* ```
|
||||
* It is not prohibited to put extra zeros at end of the list (as for \(0x^3\) and \(0x^4\) in the example). But the
|
||||
* longer the coefficients list the worse performance of arithmetical operations performed on it. Thus, it is
|
||||
* recommended not to put (or even to remove) extra (or useless) coefficients at the end of the coefficients list.
|
||||
*
|
||||
* @usesMathJax
|
||||
*/
|
||||
public val coefficients: List<C>
|
||||
) {
|
||||
override fun toString(): String = "Polynomial$coefficients"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [Polynomial] instance with given [coefficients].
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <T> Polynomial(vararg coefficients: T): Polynomial<T> = Polynomial(coefficients.toList())
|
||||
|
||||
/**
|
||||
* Evaluates the value of the given double polynomial for given double argument.
|
||||
*/
|
||||
public fun Polynomial<Double>.value(arg: Double): Double = coefficients.reduceIndexed { index, acc, c ->
|
||||
acc + c * arg.pow(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the value of the given polynomial for given argument.
|
||||
* https://en.wikipedia.org/wiki/Horner%27s_method
|
||||
*/
|
||||
public fun <T, C : Ring<T>> Polynomial<T>.value(ring: C, arg: T): T = ring {
|
||||
if (coefficients.isEmpty()) return@ring zero
|
||||
var result: T = coefficients.last()
|
||||
for (j in coefficients.size - 2 downTo 0) {
|
||||
result = (arg * result) + coefficients[j]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent the polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <T, C : Ring<T>> Polynomial<T>.asFunction(ring: C): (T) -> T = { value(ring, it) }
|
||||
|
||||
/**
|
||||
* Create a polynomial witch represents differentiated version of this polynomial
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <T, A> Polynomial<T>.differentiate(
|
||||
algebra: A,
|
||||
): Polynomial<T> where A : Ring<T>, A : NumericAlgebra<T> = algebra {
|
||||
Polynomial(coefficients.drop(1).mapIndexed { index, t -> number(index) * t })
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a polynomial witch represents indefinite integral version of this polynomial
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <T, A> Polynomial<T>.integrate(
|
||||
algebra: A,
|
||||
): Polynomial<T> where A : Field<T>, A : NumericAlgebra<T> = algebra {
|
||||
val integratedCoefficients = buildList(coefficients.size + 1) {
|
||||
add(zero)
|
||||
coefficients.forEachIndexed{ index, t -> add(t / (number(index) + one)) }
|
||||
}
|
||||
Polynomial(integratedCoefficients)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a definite integral of a given polynomial in a [range]
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <T : Comparable<T>> Polynomial<T>.integrate(
|
||||
algebra: Field<T>,
|
||||
range: ClosedRange<T>,
|
||||
): T = algebra {
|
||||
val integral = integrate(algebra)
|
||||
integral.value(algebra, range.endInclusive) - integral.value(algebra, range.start)
|
||||
}
|
||||
|
||||
/**
|
||||
* Space of polynomials.
|
||||
* Arithmetic context for univariate polynomials with coefficients stored as a [List] constructed with the provided
|
||||
* [ring] of constants.
|
||||
*
|
||||
* @param T the type of operated polynomials.
|
||||
* @param C the intersection of [Ring] of [T] and [ScaleOperations] of [T].
|
||||
* @param ring the [C] instance.
|
||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public class PolynomialSpace<T, C>(
|
||||
private val ring: C,
|
||||
) : Group<Polynomial<T>>, ScaleOperations<Polynomial<T>> where C : Ring<T>, C : ScaleOperations<T> {
|
||||
override val zero: Polynomial<T> = Polynomial(emptyList())
|
||||
|
||||
override fun Polynomial<T>.unaryMinus(): Polynomial<T> = ring {
|
||||
Polynomial(coefficients.map { -it })
|
||||
}
|
||||
|
||||
override fun add(left: Polynomial<T>, right: Polynomial<T>): Polynomial<T> {
|
||||
val dim = max(left.coefficients.size, right.coefficients.size)
|
||||
|
||||
return ring {
|
||||
Polynomial(List(dim) { index ->
|
||||
left.coefficients.getOrElse(index) { zero } + right.coefficients.getOrElse(index) { zero }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun scale(a: Polynomial<T>, value: Double): Polynomial<T> =
|
||||
ring { Polynomial(List(a.coefficients.size) { index -> a.coefficients[index] * value }) }
|
||||
public open class PolynomialSpace<C, A>(
|
||||
/**
|
||||
* Underlying ring of constants. Its operations on constants are used by local operations on constants and polynomials.
|
||||
*/
|
||||
public val ring: A,
|
||||
) : Ring<Polynomial<C>>, ScaleOperations<Polynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
|
||||
|
||||
/**
|
||||
* Evaluates the polynomial for the given value [arg].
|
||||
* Instance of zero constant (zero of the underlying ring).
|
||||
*/
|
||||
public operator fun Polynomial<T>.invoke(arg: T): T = value(ring, arg)
|
||||
public val constantZero: C get() = ring.zero
|
||||
/**
|
||||
* Instance of unit constant (unit of the underlying ring).
|
||||
*/
|
||||
public val constantOne: C get() = ring.one
|
||||
|
||||
public fun Polynomial<T>.asFunction(): (T) -> T = asFunction(ring)
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public operator fun C.plus(other: Polynomial<C>): Polynomial<C> =
|
||||
with(ring) {
|
||||
with(other.coefficients) {
|
||||
if (isEmpty()) Polynomial(listOf(this@plus))
|
||||
else Polynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
val result = if (size == 0) this@plus else this@plus + get(0)
|
||||
|
||||
}
|
||||
|
||||
public inline fun <T, C, R> C.polynomial(block: PolynomialSpace<T, C>.() -> R): R where C : Ring<T>, C : ScaleOperations<T> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return PolynomialSpace(this).block()
|
||||
if (size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public operator fun C.minus(other: Polynomial<C>): Polynomial<C> =
|
||||
with(ring) {
|
||||
with(other.coefficients) {
|
||||
if (isEmpty()) Polynomial(listOf(this@minus))
|
||||
else Polynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
(1..lastIndex).forEach { this[it] = -this[it] }
|
||||
|
||||
val result = if (size == 0) this@minus else this@minus - get(0)
|
||||
|
||||
if (size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public operator fun C.times(other: Polynomial<C>): Polynomial<C> =
|
||||
with(ring) {
|
||||
Polynomial(
|
||||
other.coefficients
|
||||
.toMutableList()
|
||||
.apply {
|
||||
for (deg in indices) this[deg] = this@times * this[deg]
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public operator fun Polynomial<C>.plus(other: C): Polynomial<C> =
|
||||
with(ring) {
|
||||
with(coefficients) {
|
||||
if (isEmpty()) Polynomial(listOf(other))
|
||||
else Polynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
val result = if (size == 0) other else get(0) + other
|
||||
|
||||
if (size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public operator fun Polynomial<C>.minus(other: C): Polynomial<C> =
|
||||
with(ring) {
|
||||
with(coefficients) {
|
||||
if (isEmpty()) Polynomial(listOf(-other))
|
||||
else Polynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
val result = if (size == 0) other else get(0) - other
|
||||
|
||||
if (size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public operator fun Polynomial<C>.times(other: C): Polynomial<C> =
|
||||
with(ring) {
|
||||
Polynomial(
|
||||
coefficients
|
||||
.toMutableList()
|
||||
.apply {
|
||||
for (deg in indices) this[deg] = this[deg] * other
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the constant [value] to polynomial.
|
||||
*/
|
||||
public fun number(value: C): Polynomial<C> = Polynomial(listOf(value))
|
||||
/**
|
||||
* Converts the constant to polynomial.
|
||||
*/
|
||||
public fun C.asPolynomial(): Polynomial<C> = number(this)
|
||||
|
||||
/**
|
||||
* Returns negation of the polynomial.
|
||||
*/
|
||||
public override operator fun Polynomial<C>.unaryMinus(): Polynomial<C> = ring {
|
||||
Polynomial(coefficients.map { -it })
|
||||
}
|
||||
/**
|
||||
* Returns sum of the polynomials.
|
||||
*/
|
||||
public override operator fun Polynomial<C>.plus(other: Polynomial<C>): Polynomial<C> = ring {
|
||||
val thisDegree = degree
|
||||
val otherDegree = other.degree
|
||||
return Polynomial(
|
||||
List(max(thisDegree, otherDegree) + 1) {
|
||||
when {
|
||||
it > thisDegree -> other.coefficients[it]
|
||||
it > otherDegree -> coefficients[it]
|
||||
else -> coefficients[it] + other.coefficients[it]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns difference of the polynomials.
|
||||
*/
|
||||
public override operator fun Polynomial<C>.minus(other: Polynomial<C>): Polynomial<C> = ring {
|
||||
val thisDegree = degree
|
||||
val otherDegree = other.degree
|
||||
return Polynomial(
|
||||
List(max(thisDegree, otherDegree) + 1) {
|
||||
when {
|
||||
it > thisDegree -> -other.coefficients[it]
|
||||
it > otherDegree -> coefficients[it]
|
||||
else -> coefficients[it] - other.coefficients[it]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns product of the polynomials.
|
||||
*/
|
||||
public override operator fun Polynomial<C>.times(other: Polynomial<C>): Polynomial<C> = ring {
|
||||
val thisDegree = degree
|
||||
val otherDegree = other.degree
|
||||
return Polynomial(
|
||||
List(thisDegree + otherDegree + 1) { d ->
|
||||
(max(0, d - otherDegree)..min(thisDegree, d))
|
||||
.map { coefficients[it] * other.coefficients[d - it] }
|
||||
.reduce { acc, rational -> acc + rational }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance of zero polynomial (zero of the polynomial ring).
|
||||
*/
|
||||
override val zero: Polynomial<C> = Polynomial(emptyList())
|
||||
/**
|
||||
* Instance of unit polynomial (unit of the polynomial ring).
|
||||
*/
|
||||
override val one: Polynomial<C> by lazy { Polynomial(listOf(constantOne)) }
|
||||
|
||||
/**
|
||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
||||
* zero, degree is -1.
|
||||
*/
|
||||
public val Polynomial<C>.degree: Int get() = coefficients.lastIndex
|
||||
|
||||
override fun add(left: Polynomial<C>, right: Polynomial<C>): Polynomial<C> = left + right
|
||||
override fun multiply(left: Polynomial<C>, right: Polynomial<C>): Polynomial<C> = left * right
|
||||
override fun scale(a: Polynomial<C>, value: Double): Polynomial<C> =
|
||||
ring { Polynomial(a.coefficients.map { scale(it, value) }) }
|
||||
|
||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
||||
// [ListPolynomialSpace] as a context receiver
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [argument].
|
||||
*/
|
||||
public inline fun Polynomial<C>.substitute(argument: C): C = value(ring, argument)
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun Polynomial<C>.asFunction(): (C) -> C = asFunctionOver(ring)
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [argument].
|
||||
*/
|
||||
public inline operator fun Polynomial<C>.invoke(argument: C): C = value(ring, argument)
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a [Polynomial] instance with provided [coefficients]. The collection of coefficients will be reversed
|
||||
* if [reverse] parameter is true.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C> Polynomial(coefficients: List<C>, reverse: Boolean = false): Polynomial<C> =
|
||||
Polynomial(with(coefficients) { if (reverse) reversed() else this })
|
||||
|
||||
/**
|
||||
* Constructs a [Polynomial] instance with provided [coefficients]. The collection of coefficients will be reversed
|
||||
* if [reverse] parameter is true.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C> Polynomial(vararg coefficients: C, reverse: Boolean = false): Polynomial<C> =
|
||||
Polynomial(with(coefficients) { if (reverse) reversed() else toList() })
|
||||
|
||||
/**
|
||||
* Represents [this] constant as a [Polynomial].
|
||||
*/
|
||||
public fun <C> C.asPolynomial() : Polynomial<C> = Polynomial(listOf(this))
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.*
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.math.max
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
/**
|
||||
* Creates a [PolynomialSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A> A.polynomialSpace: PolynomialSpace<C, A> where A : Ring<C>, A : ScaleOperations<C>
|
||||
get() = PolynomialSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [PolynomialSpace]'s scope over a received ring.
|
||||
*/ // TODO: When context will be ready move [ListPolynomialSpace] and add [A] to context receivers of [block]
|
||||
public inline fun <C, A, R> A.polynomialSpace(block: PolynomialSpace<C, A>.() -> R): R where A : Ring<C>, A : ScaleOperations<C> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return PolynomialSpace(this).block()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] Double polynomial on provided Double argument.
|
||||
*/
|
||||
public fun Polynomial<Double>.value(arg: Double): Double =
|
||||
coefficients.reduceIndexedOrNull { index, acc, c ->
|
||||
acc + c * arg.pow(index)
|
||||
} ?: .0
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided argument.
|
||||
*
|
||||
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
|
||||
*/
|
||||
public fun <C> Polynomial<C>.value(ring: Ring<C>, arg: C): C = ring {
|
||||
if (coefficients.isEmpty()) return zero
|
||||
var result: C = coefficients.last()
|
||||
for (j in coefficients.size - 2 downTo 0) {
|
||||
result = (arg * result) + coefficients[j]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> Polynomial<C>.asFunctionOver(ring: A): (C) -> C = { value(ring, it) }
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> Polynomial<C>.differentiate(
|
||||
ring: A,
|
||||
): Polynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = ring {
|
||||
Polynomial(
|
||||
buildList(max(0, coefficients.size - 1)) {
|
||||
for (deg in 1 .. coefficients.lastIndex) add(number(deg) * coefficients[deg])
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic antiderivative of received polynomial.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> Polynomial<C>.integrate(
|
||||
ring: A,
|
||||
): Polynomial<C> where A : Field<C>, A : NumericAlgebra<C> = ring {
|
||||
Polynomial(
|
||||
buildList(coefficients.size + 1) {
|
||||
add(zero)
|
||||
coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a definite integral of [this] polynomial in the specified [range].
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C : Comparable<C>> Polynomial<C>.integrate(
|
||||
ring: Field<C>,
|
||||
range: ClosedRange<C>,
|
||||
): C = ring {
|
||||
val antiderivative = integrate(ring)
|
||||
return antiderivative.value(ring, range.endInclusive) - antiderivative.value(ring, range.start)
|
||||
}
|
@ -3,15 +3,301 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("LocalVariableName")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import space.kscience.kmath.functions.testUtils.*
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
class PolynomialTest {
|
||||
@Test
|
||||
fun testIntegration() {
|
||||
val polynomial = Polynomial(1.0, -2.0, 1.0)
|
||||
assertEquals(0.0, polynomial.value(1.0), 0.001)
|
||||
fun test_Polynomial_Constant_plus() {
|
||||
RationalField.polynomialSpace {
|
||||
assertEquals(
|
||||
Polynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + Rational(-3),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
Polynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + Rational(2),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Polynomial(Rational(-2)) + Rational(2),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Polynomial<Rational>() + Rational(0),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
|
||||
Polynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + Rational(1),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-1)),
|
||||
Polynomial(Rational(-2)) + Rational(1),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(2)),
|
||||
Polynomial<Rational>() + Rational(2),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Constant_minus() {
|
||||
RationalField.polynomialSpace {
|
||||
assertEquals(
|
||||
Polynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) - Rational(-3),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
Polynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - Rational(2),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Polynomial(Rational(2)) - Rational(2),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Polynomial<Rational>() - Rational(0),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
|
||||
Polynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - Rational(1),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(1)),
|
||||
Polynomial(Rational(2)) - Rational(1),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-2)),
|
||||
Polynomial<Rational>() - Rational(2),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Constant_times() {
|
||||
IntModuloRing(35).polynomialSpace {
|
||||
assertEquals(
|
||||
Polynomial(34, 2, 1, 20, 2),
|
||||
Polynomial(22, 26, 13, 15, 26) * m(27),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(0, 0, 0, 0, 0),
|
||||
Polynomial(7, 0, 49, 21, 14) * m(15),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Constant_Polynomial_plus() {
|
||||
RationalField.polynomialSpace {
|
||||
assertEquals(
|
||||
Polynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
Rational(-3) + Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(2) + Polynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Rational(2) + Polynomial(Rational(-2)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Rational(0) + Polynomial(),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(1) + Polynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-1)),
|
||||
Rational(1) + Polynomial(Rational(-2)),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(2)),
|
||||
Rational(2) + Polynomial(),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Constant_Polynomial_minus() {
|
||||
RationalField.polynomialSpace {
|
||||
assertEquals(
|
||||
Polynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
Rational(3) - Polynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(-2) - Polynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Rational(-2) - Polynomial(Rational(-2)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0)),
|
||||
Rational(0) - Polynomial(),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(-1) - Polynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(1)),
|
||||
Rational(-1) - Polynomial(Rational(-2)),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-2)),
|
||||
Rational(-2) - Polynomial(),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Constant_Polynomial_times() {
|
||||
IntModuloRing(35).polynomialSpace {
|
||||
assertEquals(
|
||||
Polynomial(34, 2, 1, 20, 2),
|
||||
m(27) * Polynomial(22, 26, 13, 15, 26),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(0, 0, 0, 0, 0),
|
||||
m(15) * Polynomial(7, 0, 49, 21, 14),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_unaryMinus() {
|
||||
RationalField.polynomialSpace {
|
||||
assertEquals(
|
||||
Polynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
|
||||
-Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7), Rational(0), Rational(0)),
|
||||
-Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Polynomial_plus() {
|
||||
RationalField.polynomialSpace {
|
||||
// (5/9 - 8/9 x - 8/7 x^2) + (-5/7 + 5/1 x + 5/8 x^2) ?= -10/63 + 37/9 x - 29/56 x^2
|
||||
assertEquals(
|
||||
Polynomial(Rational(-10, 63), Rational(37, 9), Rational(-29, 56)),
|
||||
Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) +
|
||||
Polynomial(Rational(-5, 7), Rational(5, 1), Rational(5, 8)),
|
||||
"test 1"
|
||||
)
|
||||
// (-2/9 - 8/3 x) + (0 + 9/4 x + 2/4 x^2) ?= -2/9 - 5/12 x + 2/4 x^2
|
||||
assertEquals(
|
||||
Polynomial(Rational(-2, 9), Rational(-5, 12), Rational(2, 4)),
|
||||
Polynomial(Rational(-2, 9), Rational(-8, 3)) +
|
||||
Polynomial(Rational(0), Rational(9, 4), Rational(2, 4)),
|
||||
"test 2"
|
||||
)
|
||||
// (-4/7 - 2/6 x + 0 x^2 + 0 x^3) + (-6/3 - 7/2 x + 2/3 x^2) ?= -18/7 - 23/6 x + 2/3 x^2
|
||||
assertEquals(
|
||||
Polynomial(Rational(-18, 7), Rational(-23, 6), Rational(2, 3), Rational(0)),
|
||||
Polynomial(Rational(-4, 7), Rational(-2, 6), Rational(0), Rational(0)) +
|
||||
Polynomial(Rational(-6, 3), Rational(-7, 2), Rational(2, 3)),
|
||||
"test 3"
|
||||
)
|
||||
// (-2/4 - 6/9 x - 4/9 x^2) + (2/4 + 6/9 x + 4/9 x^2) ?= 0
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0)),
|
||||
Polynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)) +
|
||||
Polynomial(Rational(2, 4), Rational(6, 9), Rational(4, 9)),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Polynomial_minus() {
|
||||
RationalField.polynomialSpace {
|
||||
// (5/9 - 8/9 x - 8/7 x^2) - (-5/7 + 5/1 x + 5/8 x^2) ?= 80/63 - 53/9 x - 99/56 x^2
|
||||
assertEquals(
|
||||
Polynomial(Rational(80, 63), Rational(-53, 9), Rational(-99, 56)),
|
||||
Polynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) -
|
||||
Polynomial(Rational(-5, 7), Rational(5, 1), Rational(5, 8)),
|
||||
"test 1"
|
||||
)
|
||||
// (-2/9 - 8/3 x) - (0 + 9/4 x + 2/4 x^2) ?= -2/9 - 59/12 x - 2/4 x^2
|
||||
assertEquals(
|
||||
Polynomial(Rational(-2, 9), Rational(-59, 12), Rational(-2, 4)),
|
||||
Polynomial(Rational(-2, 9), Rational(-8, 3)) -
|
||||
Polynomial(Rational(0), Rational(9, 4), Rational(2, 4)),
|
||||
"test 2"
|
||||
)
|
||||
// (-4/7 - 2/6 x + 0 x^2 + 0 x^3) - (-6/3 - 7/2 x + 2/3 x^2) ?= 10/7 + 19/6 x - 2/3 x^2
|
||||
assertEquals(
|
||||
Polynomial(Rational(10, 7), Rational(19, 6), Rational(-2, 3), Rational(0)),
|
||||
Polynomial(Rational(-4, 7), Rational(-2, 6), Rational(0), Rational(0)) -
|
||||
Polynomial(Rational(-6, 3), Rational(-7, 2), Rational(2, 3)),
|
||||
"test 3"
|
||||
)
|
||||
// (-2/4 - 6/9 x - 4/9 x^2) - (-2/4 - 6/9 x - 4/9 x^2) ?= 0
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0)),
|
||||
Polynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)) -
|
||||
Polynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Polynomial_times() {
|
||||
IntModuloRing(35).polynomialSpace {
|
||||
// (1 + x + x^2) * (1 - x + x^2) ?= 1 + x^2 + x^4
|
||||
assertEquals(
|
||||
Polynomial(1, 0, 1, 0, 1),
|
||||
Polynomial(1, -1, 1) * Polynomial(1, 1, 1),
|
||||
"test 1"
|
||||
)
|
||||
// Spoiler: 5 * 7 = 0
|
||||
assertEquals(
|
||||
Polynomial(0, 0, 0, 0, 0),
|
||||
Polynomial(5, -25, 10) * Polynomial(21, 14, -7),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.functions.testUtils.Rational
|
||||
import space.kscience.kmath.functions.testUtils.RationalField
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
class PolynomialUtilTest {
|
||||
@Test
|
||||
fun test_Polynomial_value_Double() {
|
||||
assertEquals(
|
||||
0.0,
|
||||
Polynomial(1.0, -2.0, 1.0).value(1.0),
|
||||
0.001,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
0.0,
|
||||
Polynomial(1.0, -2.0, 1.0).value(1.0),
|
||||
0.001,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
1.1931904761904761,
|
||||
Polynomial(0.625, 2.6666666666666665, 0.5714285714285714, 1.5).value(0.2),
|
||||
0.001,
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
0.5681904761904762,
|
||||
Polynomial(0.0, 2.6666666666666665, 0.5714285714285714, 1.5).value(0.2),
|
||||
0.001,
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
1.1811904761904761,
|
||||
Polynomial(0.625, 2.6666666666666665, 0.5714285714285714, 0.0).value(0.2),
|
||||
0.001,
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
1.1703333333333332,
|
||||
Polynomial(0.625, 2.6666666666666665, 0.0, 1.5).value(0.2),
|
||||
0.001,
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_value_Constant() {
|
||||
assertEquals(
|
||||
Rational(0),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).value(RationalField, Rational(1)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(25057, 21000),
|
||||
Polynomial(Rational(5, 8), Rational(8, 3), Rational(4, 7), Rational(3, 2))
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(2983, 5250),
|
||||
Polynomial(Rational(0), Rational(8, 3), Rational(4, 7), Rational(3, 2))
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(4961, 4200),
|
||||
Polynomial(Rational(5, 8), Rational(8, 3), Rational(4, 7), Rational(0))
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(3511, 3000),
|
||||
Polynomial(Rational(5, 8), Rational(8, 3), Rational(0), Rational(3, 2))
|
||||
.value(RationalField, Rational(1, 5)),
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_differentiate() {
|
||||
assertEquals(
|
||||
Polynomial(Rational(-2), Rational(2)),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).differentiate(RationalField),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).differentiate(RationalField),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
|
||||
Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).differentiate(RationalField),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(0)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).differentiate(RationalField),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_integrate() {
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)),
|
||||
Polynomial(Rational(1), Rational(-2), Rational(1)).integrate(RationalField),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).integrate(RationalField),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(0), Rational(0), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
|
||||
Polynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).integrate(RationalField),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Polynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(0)),
|
||||
Polynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).integrate(RationalField),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.operations.ScaleOperations
|
||||
|
||||
|
||||
class IntModulo {
|
||||
val residue: Int
|
||||
val modulus: Int
|
||||
|
||||
@PublishedApi
|
||||
internal constructor(residue: Int, modulus: Int, toCheckInput: Boolean = true) {
|
||||
if (toCheckInput) {
|
||||
require(modulus != 0) { "modulus can not be zero" }
|
||||
this.modulus = if (modulus < 0) -modulus else modulus
|
||||
this.residue = residue.mod(this.modulus)
|
||||
} else {
|
||||
this.residue = residue
|
||||
this.modulus = modulus
|
||||
}
|
||||
}
|
||||
|
||||
constructor(residue: Int, modulus: Int) : this(residue, modulus, true)
|
||||
|
||||
operator fun unaryPlus(): IntModulo = this
|
||||
operator fun unaryMinus(): IntModulo =
|
||||
IntModulo(
|
||||
if (residue == 0) 0 else modulus - residue,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun plus(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not add two residue different modulo" }
|
||||
return IntModulo(
|
||||
(residue + other.residue) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun plus(other: Int): IntModulo =
|
||||
IntModulo(
|
||||
(residue + other) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun minus(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not subtract two residue different modulo" }
|
||||
return IntModulo(
|
||||
(residue - other.residue) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun minus(other: Int): IntModulo =
|
||||
IntModulo(
|
||||
(residue - other) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun times(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not multiply two residue different modulo" }
|
||||
return IntModulo(
|
||||
(residue * other.residue) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun times(other: Int): IntModulo =
|
||||
IntModulo(
|
||||
(residue * other) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun div(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not divide two residue different modulo" }
|
||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other.residue, modulus)
|
||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
||||
return IntModulo(
|
||||
(residue * reciprocalCandidate) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun div(other: Int): IntModulo {
|
||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other, modulus)
|
||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
||||
return IntModulo(
|
||||
(residue * reciprocalCandidate) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean =
|
||||
when (other) {
|
||||
is IntModulo -> residue == other.residue && modulus == other.modulus
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = residue.hashCode()
|
||||
|
||||
override fun toString(): String = "$residue mod $modulus"
|
||||
}
|
||||
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE")
|
||||
class IntModuloRing : Ring<IntModulo>, ScaleOperations<IntModulo> {
|
||||
|
||||
val modulus: Int
|
||||
|
||||
constructor(modulus: Int) {
|
||||
require(modulus != 0) { "modulus can not be zero" }
|
||||
this.modulus = if (modulus < 0) -modulus else modulus
|
||||
}
|
||||
|
||||
override inline val zero: IntModulo get() = IntModulo(0, modulus, toCheckInput = false)
|
||||
override inline val one: IntModulo get() = IntModulo(1, modulus, toCheckInput = false)
|
||||
|
||||
fun number(arg: Int): IntModulo = IntModulo(arg, modulus, toCheckInput = false)
|
||||
|
||||
override inline fun add(left: IntModulo, right: IntModulo): IntModulo = left + right
|
||||
override inline fun multiply(left: IntModulo, right: IntModulo): IntModulo = left * right
|
||||
|
||||
override inline fun IntModulo.unaryMinus(): IntModulo = -this
|
||||
override inline fun IntModulo.plus(arg: IntModulo): IntModulo = this + arg
|
||||
override inline fun IntModulo.minus(arg: IntModulo): IntModulo = this - arg
|
||||
override inline fun IntModulo.times(arg: IntModulo): IntModulo = this * arg
|
||||
inline fun IntModulo.div(arg: IntModulo): IntModulo = this / arg
|
||||
|
||||
override fun scale(a: IntModulo, value: Double): IntModulo = a * value.toInt()
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.functions.Polynomial
|
||||
import space.kscience.kmath.functions.PolynomialSpace
|
||||
|
||||
|
||||
fun PolynomialSpace<IntModulo, IntModuloRing>.Polynomial(vararg coefs: Int): Polynomial<IntModulo> =
|
||||
Polynomial(coefs.map { IntModulo(it, ring.modulus) })
|
||||
fun IntModuloRing.Polynomial(vararg coefs: Int): Polynomial<IntModulo> =
|
||||
Polynomial(coefs.map { IntModulo(it, modulus) })
|
||||
|
||||
fun IntModuloRing.m(arg: Int): IntModulo = IntModulo(arg, modulus)
|
||||
fun PolynomialSpace<IntModulo, IntModuloRing>.m(arg: Int): IntModulo = IntModulo(arg, ring.modulus)
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.Field
|
||||
import space.kscience.kmath.operations.NumbersAddOps
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
class Rational {
|
||||
companion object {
|
||||
val ZERO: Rational = Rational(0L)
|
||||
val ONE: Rational = Rational(1L)
|
||||
}
|
||||
|
||||
val numerator: Long
|
||||
val denominator: Long
|
||||
|
||||
internal constructor(numerator: Long, denominator: Long, toCheckInput: Boolean = true) {
|
||||
if (toCheckInput) {
|
||||
if (denominator == 0L) throw ArithmeticException("/ by zero")
|
||||
|
||||
val greatestCommonDivider = gcd(numerator, denominator).let { if (denominator < 0L) -it else it }
|
||||
|
||||
this.numerator = numerator / greatestCommonDivider
|
||||
this.denominator = denominator / greatestCommonDivider
|
||||
} else {
|
||||
this.numerator = numerator
|
||||
this.denominator = denominator
|
||||
}
|
||||
}
|
||||
|
||||
constructor(numerator: Int, denominator: Int) : this(numerator.toLong(), denominator.toLong(), true)
|
||||
constructor(numerator: Int, denominator: Long) : this(numerator.toLong(), denominator, true)
|
||||
constructor(numerator: Long, denominator: Int) : this(numerator, denominator.toLong(), true)
|
||||
constructor(numerator: Long, denominator: Long) : this(numerator, denominator, true)
|
||||
constructor(numerator: Int) : this(numerator.toLong(), 1L, false)
|
||||
constructor(numerator: Long) : this(numerator, 1L, false)
|
||||
|
||||
operator fun unaryPlus(): Rational = this
|
||||
operator fun unaryMinus(): Rational = Rational(-this.numerator, this.denominator)
|
||||
operator fun plus(other: Rational): Rational {
|
||||
val denominatorsGcd = gcd(denominator, other.denominator)
|
||||
val dividedThisDenominator = denominator / denominatorsGcd
|
||||
val dividedOtherDenominator = other.denominator / denominatorsGcd
|
||||
val numeratorCandidate = numerator * dividedOtherDenominator + dividedThisDenominator * other.numerator
|
||||
val secondGcd = gcd(numeratorCandidate, denominatorsGcd)
|
||||
return Rational(
|
||||
numeratorCandidate / secondGcd,
|
||||
dividedThisDenominator * (other.denominator / secondGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun plus(other: Int): Rational =
|
||||
Rational(
|
||||
numerator + denominator * other.toLong(),
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun plus(other: Long): Rational =
|
||||
Rational(
|
||||
numerator + denominator * other,
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun minus(other: Rational): Rational {
|
||||
val denominatorsGcd = gcd(denominator, other.denominator)
|
||||
val dividedThisDenominator = denominator / denominatorsGcd
|
||||
val dividedOtherDenominator = other.denominator / denominatorsGcd
|
||||
val numeratorCandidate = numerator * dividedOtherDenominator - dividedThisDenominator * other.numerator
|
||||
val secondGcd = gcd(numeratorCandidate, denominatorsGcd)
|
||||
return Rational(
|
||||
numeratorCandidate / secondGcd,
|
||||
dividedThisDenominator * (other.denominator / secondGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun minus(other: Int): Rational =
|
||||
Rational(
|
||||
numerator - denominator * other.toLong(),
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun minus(other: Long): Rational =
|
||||
Rational(
|
||||
numerator - denominator * other,
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun times(other: Rational): Rational {
|
||||
val thisDenominatorAndOtherNumeratorGcd = gcd(denominator, other.numerator)
|
||||
val otherDenominatorAndThisNumeratorGcd = gcd(other.denominator, numerator)
|
||||
return Rational(
|
||||
(numerator / otherDenominatorAndThisNumeratorGcd) * (other.numerator / thisDenominatorAndOtherNumeratorGcd),
|
||||
(denominator / thisDenominatorAndOtherNumeratorGcd) * (other.denominator / otherDenominatorAndThisNumeratorGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun times(other: Int): Rational {
|
||||
val other = other.toLong()
|
||||
val denominatorAndOtherGcd = gcd(denominator, other)
|
||||
return Rational(
|
||||
numerator * (other / denominatorAndOtherGcd),
|
||||
denominator / denominatorAndOtherGcd,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun times(other: Long): Rational {
|
||||
val denominatorAndOtherGcd = gcd(denominator, other)
|
||||
return Rational(
|
||||
numerator * (other / denominatorAndOtherGcd),
|
||||
denominator / denominatorAndOtherGcd,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun div(other: Rational): Rational {
|
||||
val denominatorsGcd = gcd(denominator, other.denominator)
|
||||
val numeratorsGcd = gcd(numerator, other.numerator)
|
||||
return Rational(
|
||||
(numerator / numeratorsGcd) * (other.denominator / denominatorsGcd),
|
||||
(denominator / denominatorsGcd) * (other.numerator / numeratorsGcd)
|
||||
)
|
||||
}
|
||||
operator fun div(other: Int): Rational {
|
||||
val other = other.toLong()
|
||||
val numeratorAndOtherGcd = gcd(numerator, other)
|
||||
return Rational(
|
||||
numerator / numeratorAndOtherGcd,
|
||||
denominator * (other / numeratorAndOtherGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun div(other: Long): Rational {
|
||||
val numeratorAndOtherGcd = gcd(numerator, other)
|
||||
return Rational(
|
||||
numerator / numeratorAndOtherGcd,
|
||||
denominator * (other / numeratorAndOtherGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean =
|
||||
when (other) {
|
||||
is Rational -> numerator == other.numerator && denominator == other.denominator
|
||||
is Int -> numerator == other && denominator == 1L
|
||||
is Long -> numerator == other && denominator == 1L
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = 31 * numerator.hashCode() + denominator.hashCode()
|
||||
|
||||
override fun toString(): String = if (denominator == 1L) "$numerator" else "$numerator/$denominator"
|
||||
}
|
||||
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE")
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
object RationalField : Field<Rational>, NumbersAddOps<Rational> {
|
||||
override inline val zero: Rational get() = Rational.ZERO
|
||||
override inline val one: Rational get() = Rational.ONE
|
||||
|
||||
override inline fun number(value: Number): Rational = Rational(value.toLong())
|
||||
|
||||
override inline fun add(left: Rational, right: Rational): Rational = left + right
|
||||
override inline fun multiply(left: Rational, right: Rational): Rational = left * right
|
||||
override inline fun divide(left: Rational, right: Rational): Rational = left / right
|
||||
override inline fun scale(a: Rational, value: Double): Rational = a * number(value)
|
||||
|
||||
override inline fun Rational.unaryMinus(): Rational = -this
|
||||
override inline fun Rational.plus(arg: Rational): Rational = this + arg
|
||||
override inline fun Rational.minus(arg: Rational): Rational = this - arg
|
||||
override inline fun Rational.times(arg: Rational): Rational = this * arg
|
||||
override inline fun Rational.div(arg: Rational): Rational = this / arg
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
internal data class BezoutIdentityWithGCD<T>(val first: T, val second: T, val gcd: T)
|
||||
|
||||
internal tailrec fun gcd(a: Long, b: Long): Long = if (a == 0L) abs(b) else gcd(b % a, a)
|
||||
|
||||
internal fun bezoutIdentityWithGCD(a: Int, b: Int): BezoutIdentityWithGCD<Int> =
|
||||
when {
|
||||
a < 0 && b < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, -second, gcd) }
|
||||
a < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, second, gcd) }
|
||||
b < 0 -> with(bezoutIdentityWithGCDInternalLogic(a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(first, -second, gcd) }
|
||||
else -> bezoutIdentityWithGCDInternalLogic(a, b, 1, 0, 0, 1)
|
||||
}
|
||||
|
||||
internal tailrec fun bezoutIdentityWithGCDInternalLogic(a: Int, b: Int, m1: Int, m2: Int, m3: Int, m4: Int): BezoutIdentityWithGCD<Int> =
|
||||
if (b == 0) BezoutIdentityWithGCD(m1, m3, a)
|
||||
else {
|
||||
val quotient = a / b
|
||||
val reminder = a % b
|
||||
bezoutIdentityWithGCDInternalLogic(b, reminder, m2, m1 - quotient * m2, m4, m3 - quotient * m4)
|
||||
}
|
50
kmath-polynomial/README.md
Normal file
50
kmath-polynomial/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Module kmath-polynomial
|
||||
|
||||
Polynomials, rational functions, and utilities
|
||||
|
||||
## Features
|
||||
|
||||
- [polynomial abstraction](src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt) : Abstraction for polynomial spaces.
|
||||
- [rational function abstraction](src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt) : Abstraction for rational functions spaces.
|
||||
- ["list" polynomials](src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt) : List implementation of univariate polynomials.
|
||||
- ["list" rational functions](src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt) : List implementation of univariate rational functions.
|
||||
- ["list" polynomials and rational functions constructors](src/commonMain/kotlin/space/kscience/kmath/functions/listConstructors.kt) : Constructors for list polynomials and rational functions.
|
||||
- ["list" polynomials and rational functions utilities](src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt) : Utilities for list polynomials and rational functions.
|
||||
- ["numbered" polynomials](src/commonMain/kotlin/space/kscience/kmath/functions/NumberedRationalFunction.kt) : Numbered implementation of multivariate polynomials.
|
||||
- ["numbered" rational functions](src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt) : Numbered implementation of multivariate rational functions.
|
||||
- ["numbered" polynomials and rational functions constructors](src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt) : Constructors for numbered polynomials and rational functions.
|
||||
- ["numbered" polynomials and rational functions utilities](src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt) : Utilities for numbered polynomials and rational functions.
|
||||
- ["labeled" polynomials](src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt) : Labeled implementation of multivariate polynomials.
|
||||
- ["labeled" rational functions](src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt) : Labeled implementation of multivariate rational functions.
|
||||
- ["labeled" polynomials and rational functions constructors](src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt) : Constructors for labeled polynomials and rational functions.
|
||||
- ["labeled" polynomials and rational functions utilities](src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt) : Utilities for labeled polynomials and rational functions.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
## Artifact:
|
||||
|
||||
The Maven coordinates of this project are `space.kscience:kmath-polynomial:0.3.1-dev-1`.
|
||||
|
||||
**Gradle Groovy:**
|
||||
```groovy
|
||||
repositories {
|
||||
maven { url 'https://repo.kotlin.link' }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'space.kscience:kmath-polynomial:0.3.1-dev-1'
|
||||
}
|
||||
```
|
||||
**Gradle Kotlin DSL:**
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.kotlin.link")
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("space.kscience:kmath-polynomial:0.3.1-dev-1")
|
||||
}
|
||||
```
|
67
kmath-polynomial/build.gradle.kts
Normal file
67
kmath-polynomial/build.gradle.kts
Normal file
@ -0,0 +1,67 @@
|
||||
plugins {
|
||||
kotlin("multiplatform")
|
||||
id("ru.mipt.npm.gradle.common")
|
||||
id("ru.mipt.npm.gradle.native")
|
||||
}
|
||||
|
||||
description = "Polynomials, rational functions, and utilities"
|
||||
|
||||
kotlin.sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api(projects.kmathCore)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dokkaPlugin("org.jetbrains.dokka:mathjax-plugin:${npmlibs.versions.dokka.get()}")
|
||||
}
|
||||
|
||||
readme {
|
||||
maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE
|
||||
propertyByTemplate("artifact", rootProject.file("docs/templates/ARTIFACT-TEMPLATE.md"))
|
||||
|
||||
feature("polynomial abstraction", "src/commonMain/kotlin/space/kscience/kmath/functions/Polynomial.kt") {
|
||||
"Abstraction for polynomial spaces."
|
||||
}
|
||||
feature("rational function abstraction", "src/commonMain/kotlin/space/kscience/kmath/functions/RationalFunction.kt") {
|
||||
"Abstraction for rational functions spaces."
|
||||
}
|
||||
feature("\"list\" polynomials", "src/commonMain/kotlin/space/kscience/kmath/functions/ListRationalFunction.kt") {
|
||||
"List implementation of univariate polynomials."
|
||||
}
|
||||
feature("\"list\" rational functions", "src/commonMain/kotlin/space/kscience/kmath/functions/ListPolynomial.kt") {
|
||||
"List implementation of univariate rational functions."
|
||||
}
|
||||
feature("\"list\" polynomials and rational functions constructors", "src/commonMain/kotlin/space/kscience/kmath/functions/listConstructors.kt") {
|
||||
"Constructors for list polynomials and rational functions."
|
||||
}
|
||||
feature("\"list\" polynomials and rational functions utilities", "src/commonMain/kotlin/space/kscience/kmath/functions/listUtil.kt") {
|
||||
"Utilities for list polynomials and rational functions."
|
||||
}
|
||||
feature("\"numbered\" polynomials", "src/commonMain/kotlin/space/kscience/kmath/functions/NumberedRationalFunction.kt") {
|
||||
"Numbered implementation of multivariate polynomials."
|
||||
}
|
||||
feature("\"numbered\" rational functions", "src/commonMain/kotlin/space/kscience/kmath/functions/NumberedPolynomial.kt") {
|
||||
"Numbered implementation of multivariate rational functions."
|
||||
}
|
||||
feature("\"numbered\" polynomials and rational functions constructors", "src/commonMain/kotlin/space/kscience/kmath/functions/numberedConstructors.kt") {
|
||||
"Constructors for numbered polynomials and rational functions."
|
||||
}
|
||||
feature("\"numbered\" polynomials and rational functions utilities", "src/commonMain/kotlin/space/kscience/kmath/functions/numberedUtil.kt") {
|
||||
"Utilities for numbered polynomials and rational functions."
|
||||
}
|
||||
feature("\"labeled\" polynomials", "src/commonMain/kotlin/space/kscience/kmath/functions/LabeledRationalFunction.kt") {
|
||||
"Labeled implementation of multivariate polynomials."
|
||||
}
|
||||
feature("\"labeled\" rational functions", "src/commonMain/kotlin/space/kscience/kmath/functions/LabeledPolynomial.kt") {
|
||||
"Labeled implementation of multivariate rational functions."
|
||||
}
|
||||
feature("\"labeled\" polynomials and rational functions constructors", "src/commonMain/kotlin/space/kscience/kmath/functions/labeledConstructors.kt") {
|
||||
"Constructors for labeled polynomials and rational functions."
|
||||
}
|
||||
feature("\"labeled\" polynomials and rational functions utilities", "src/commonMain/kotlin/space/kscience/kmath/functions/labeledUtil.kt") {
|
||||
"Utilities for labeled polynomials and rational functions."
|
||||
}
|
||||
}
|
@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.math.max
|
||||
|
||||
|
||||
/**
|
||||
* Represents multivariate polynomial that stores its coefficients in a [Map] and terms' signatures in a [Map] that
|
||||
* associates variables (of type [Symbol]) with their degree.
|
||||
*
|
||||
* @param C the type of constants.
|
||||
*/
|
||||
public data class LabeledPolynomial<out C>
|
||||
@PublishedApi
|
||||
internal constructor(
|
||||
/**
|
||||
* Map that contains coefficients of the polynomial.
|
||||
*
|
||||
* Every monomial \(a x_1^{d_1} ... x_n^{d_n}\) is stored as a pair "key-value" in the map, where the value is the
|
||||
* coefficient \(a\) and the key is a map that associates variables in the monomial with their degree in the monomial.
|
||||
* For example, coefficients of a polynomial \(5 a^2 c^3 - 6 b\) can be represented as
|
||||
* ```
|
||||
* mapOf(
|
||||
* mapOf(
|
||||
* a to 2,
|
||||
* c to 3
|
||||
* ) to 5,
|
||||
* mapOf(
|
||||
* b to 1
|
||||
* ) to (-6)
|
||||
* )
|
||||
* ```
|
||||
* and also as
|
||||
* ```
|
||||
* mapOf(
|
||||
* mapOf(
|
||||
* a to 2,
|
||||
* c to 3
|
||||
* ) to 5,
|
||||
* mapOf(
|
||||
* b to 1
|
||||
* ) to (-6),
|
||||
* mapOf(
|
||||
* b to 1,
|
||||
* c to 1
|
||||
* ) to 0
|
||||
* )
|
||||
* ```
|
||||
* where \(a\), \(b\) and \(c\) are corresponding [Symbol] objects.
|
||||
*
|
||||
* It is not prohibited to put extra zero monomials into the map (as for \(0 b c\) in the example). But the
|
||||
* bigger the coefficients map the worse performance of arithmetical operations performed on it. Thus, it is
|
||||
* recommended not to put (or even to remove) extra (or useless) monomials in the coefficients map.
|
||||
* @usesMathJax
|
||||
*/
|
||||
public val coefficients: Map<Map<Symbol, UInt>, C>
|
||||
) {
|
||||
override fun toString(): String = "LabeledPolynomial$coefficients"
|
||||
}
|
||||
|
||||
/**
|
||||
* Arithmetic context for multivariate polynomials with coefficients stored as a [Map] and terms' signatures stored as a
|
||||
* [Map] constructed with the provided [ring] of constants.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public class LabeledPolynomialSpace<C, out A : Ring<C>>(
|
||||
public override val ring: A,
|
||||
) : MultivariatePolynomialSpace<C, Symbol, LabeledPolynomial<C>>, PolynomialSpaceOverRing<C, LabeledPolynomial<C>, A> {
|
||||
/**
|
||||
* Returns sum of the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.plus(other: Int): LabeledPolynomial<C> =
|
||||
if (other == 0) LabeledPolynomialAsIs(
|
||||
mapOf(this@plus to 1U) to constantOne,
|
||||
)
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(this@plus to 1U) to constantOne,
|
||||
emptyMap<Symbol, UInt>() to other.asConstant(),
|
||||
)
|
||||
/**
|
||||
* Returns difference between the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.minus(other: Int): LabeledPolynomial<C> =
|
||||
if (other == 0) LabeledPolynomialAsIs(
|
||||
mapOf(this@minus to 1U) to constantOne,
|
||||
)
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(this@minus to 1U) to constantOne,
|
||||
emptyMap<Symbol, UInt>() to (-other).asConstant(),
|
||||
)
|
||||
/**
|
||||
* Returns product of the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.times(other: Int): LabeledPolynomial<C> =
|
||||
if (other == 0) zero
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(this to 1U) to other.asConstant(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun Int.plus(other: Symbol): LabeledPolynomial<C> =
|
||||
if (this == 0) LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to constantOne,
|
||||
)
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to constantOne,
|
||||
emptyMap<Symbol, UInt>() to this@plus.asConstant(),
|
||||
)
|
||||
/**
|
||||
* Returns difference between the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun Int.minus(other: Symbol): LabeledPolynomial<C> =
|
||||
if (this == 0) LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to -constantOne,
|
||||
)
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to -constantOne,
|
||||
emptyMap<Symbol, UInt>() to constantOne * this@minus,
|
||||
)
|
||||
/**
|
||||
* Returns product of the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun Int.times(other: Symbol): LabeledPolynomial<C> =
|
||||
if (this == 0) zero
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to this@times.asConstant(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
||||
*/
|
||||
public override operator fun LabeledPolynomial<C>.plus(other: Int): LabeledPolynomial<C> =
|
||||
when {
|
||||
other == 0 -> this
|
||||
coefficients.isEmpty() -> other.asPolynomial()
|
||||
else -> LabeledPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyMap(), other.asConstant()) { it -> it + other }
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns difference between the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
||||
*/
|
||||
public override operator fun LabeledPolynomial<C>.minus(other: Int): LabeledPolynomial<C> =
|
||||
when {
|
||||
other == 0 -> this
|
||||
coefficients.isEmpty() -> other.asPolynomial()
|
||||
else -> LabeledPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyMap(), (-other).asConstant()) { it -> it - other }
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns product of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [other] copies of [this].
|
||||
*/
|
||||
public override operator fun LabeledPolynomial<C>.times(other: Int): LabeledPolynomial<C> =
|
||||
when(other) {
|
||||
0 -> zero
|
||||
1 -> this
|
||||
else -> LabeledPolynomialAsIs(
|
||||
coefficients.mapValues { (_, value) -> value * other }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
||||
*/
|
||||
public override operator fun Int.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
when {
|
||||
this == 0 -> other
|
||||
other.coefficients.isEmpty() -> this@plus.asPolynomial()
|
||||
else -> LabeledPolynomialAsIs(
|
||||
other.coefficients.withPutOrChanged(emptyMap(), this@plus.asConstant()) { it -> this@plus + it }
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns difference between the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
||||
*/
|
||||
public override operator fun Int.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
when {
|
||||
this == 0 -> -other
|
||||
other.coefficients.isEmpty() -> this@minus.asPolynomial()
|
||||
else -> LabeledPolynomialAsIs(
|
||||
buildMap(other.coefficients.size + 1) {
|
||||
put(emptyMap(), asConstant())
|
||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC })
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns product of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [this] copies of [other].
|
||||
*/
|
||||
public override operator fun Int.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
when(this) {
|
||||
0 -> zero
|
||||
1 -> other
|
||||
else -> LabeledPolynomialAsIs(
|
||||
other.coefficients.mapValues { (_, value) -> this@times * value }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.plus(other: C): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(this@plus to 1U) to constantOne,
|
||||
emptyMap<Symbol, UInt>() to other,
|
||||
)
|
||||
/**
|
||||
* Returns difference between the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.minus(other: C): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(this@minus to 1U) to constantOne,
|
||||
emptyMap<Symbol, UInt>() to -other,
|
||||
)
|
||||
/**
|
||||
* Returns product of the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.times(other: C): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(this@times to 1U) to other,
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun C.plus(other: Symbol): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to constantOne,
|
||||
emptyMap<Symbol, UInt>() to this@plus,
|
||||
)
|
||||
/**
|
||||
* Returns difference between the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun C.minus(other: Symbol): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to -constantOne,
|
||||
emptyMap<Symbol, UInt>() to this@minus,
|
||||
)
|
||||
/**
|
||||
* Returns product of the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun C.times(other: Symbol): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(other to 1U) to this@times,
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun C.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
if (other.coefficients.isEmpty()) this@plus.asLabeledPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
other.coefficients.withPutOrChanged(emptyMap(), this@plus) { it -> this@plus + it }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun C.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
if (other.coefficients.isEmpty()) this@minus.asPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
buildMap(other.coefficients.size + 1) {
|
||||
put(emptyMap(), this@minus)
|
||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC })
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun C.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
other.coefficients.mapValues { this@times * it.value }
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun LabeledPolynomial<C>.plus(other: C): LabeledPolynomial<C> =
|
||||
if (coefficients.isEmpty()) other.asLabeledPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyMap(), other) { it -> it + other }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun LabeledPolynomial<C>.minus(other: C): LabeledPolynomial<C> =
|
||||
if (coefficients.isEmpty()) other.asLabeledPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyMap(), -other) { it -> it - other }
|
||||
)
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun LabeledPolynomial<C>.times(other: C): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
coefficients.mapValues { it.value * other }
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts the constant [value] to polynomial.
|
||||
*/
|
||||
public override fun number(value: C): LabeledPolynomial<C> = value.asLabeledPolynomial()
|
||||
|
||||
/**
|
||||
* Represents the variable as a monic monomial.
|
||||
*/
|
||||
public override operator fun Symbol.unaryPlus(): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(this to 1U) to constantOne,
|
||||
)
|
||||
/**
|
||||
* Returns negation of representation of the variable as a monic monomial.
|
||||
*/
|
||||
public override operator fun Symbol.unaryMinus(): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(this to 1U) to -constantOne,
|
||||
)
|
||||
/**
|
||||
* Returns sum of the variables represented as monic monomials.
|
||||
*/
|
||||
public override operator fun Symbol.plus(other: Symbol): LabeledPolynomial<C> =
|
||||
if (this == other) LabeledPolynomialAsIs(
|
||||
mapOf(this to 1U) to constantOne * 2
|
||||
)
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(this to 1U) to constantOne,
|
||||
mapOf(other to 1U) to constantOne,
|
||||
)
|
||||
/**
|
||||
* Returns difference between the variables represented as monic monomials.
|
||||
*/
|
||||
public override operator fun Symbol.minus(other: Symbol): LabeledPolynomial<C> =
|
||||
if (this == other) zero
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(this to 1U) to constantOne,
|
||||
mapOf(other to 1U) to -constantOne,
|
||||
)
|
||||
/**
|
||||
* Returns product of the variables represented as monic monomials.
|
||||
*/
|
||||
public override operator fun Symbol.times(other: Symbol): LabeledPolynomial<C> =
|
||||
if (this == other) LabeledPolynomialAsIs(
|
||||
mapOf(this to 2U) to constantOne
|
||||
)
|
||||
else LabeledPolynomialAsIs(
|
||||
mapOf(this to 1U, other to 1U) to constantOne,
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the variable represented as a monic monomial and the polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
if (other.coefficients.isEmpty()) this@plus.asPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
other.coefficients.withPutOrChanged(mapOf(this@plus to 1U), constantOne) { it -> constantOne + it }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the variable represented as a monic monomial and the polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
if (other.coefficients.isEmpty()) this@minus.asPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
buildMap(other.coefficients.size + 1) {
|
||||
put(mapOf(this@minus to 1U), constantOne)
|
||||
other.coefficients.copyMapToBy(this, { _, c -> -c }) { currentC, newC -> currentC - newC }
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns product of the variable represented as a monic monomial and the polynomial.
|
||||
*/
|
||||
public override operator fun Symbol.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
other.coefficients
|
||||
.mapKeys { (degs, _) -> degs.withPutOrChanged(this, 1u) { it -> it + 1u } }
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun LabeledPolynomial<C>.plus(other: Symbol): LabeledPolynomial<C> =
|
||||
if (coefficients.isEmpty()) other.asPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(mapOf(other to 1U), constantOne) { it -> it + constantOne }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun LabeledPolynomial<C>.minus(other: Symbol): LabeledPolynomial<C> =
|
||||
if (coefficients.isEmpty()) other.asPolynomial()
|
||||
else LabeledPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(mapOf(other to 1U), -constantOne) { it -> it - constantOne }
|
||||
)
|
||||
/**
|
||||
* Returns product of the polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
public override operator fun LabeledPolynomial<C>.times(other: Symbol): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
coefficients
|
||||
.mapKeys { (degs, _) -> degs.withPutOrChanged(other, 1u) { it -> it + 1u } }
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns negation of the polynomial.
|
||||
*/
|
||||
override fun LabeledPolynomial<C>.unaryMinus(): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
coefficients.mapValues { -it.value }
|
||||
)
|
||||
/**
|
||||
* Returns sum of the polynomials.
|
||||
*/
|
||||
override operator fun LabeledPolynomial<C>.plus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
mergeBy(coefficients, other.coefficients) { c1, c2 -> c1 + c2 }
|
||||
)
|
||||
/**
|
||||
* Returns difference of the polynomials.
|
||||
*/
|
||||
override operator fun LabeledPolynomial<C>.minus(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
buildMap(coefficients.size + other.coefficients.size) {
|
||||
coefficients.copyTo(this)
|
||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC })
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns product of the polynomials.
|
||||
*/
|
||||
override operator fun LabeledPolynomial<C>.times(other: LabeledPolynomial<C>): LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
buildMap(coefficients.size * other.coefficients.size) {
|
||||
for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) {
|
||||
val degs = mergeBy(degs1, degs2) { deg1, deg2 -> deg1 + deg2 }
|
||||
val c = c1 * c2
|
||||
this.putOrChange(degs, c) { it -> it + c }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Instance of zero polynomial (zero of the polynomial ring).
|
||||
*/
|
||||
override val zero: LabeledPolynomial<C> = LabeledPolynomialAsIs()
|
||||
/**
|
||||
* Instance of unit polynomial (unit of the polynomial ring).
|
||||
*/
|
||||
override val one: LabeledPolynomial<C> = constantOne.asLabeledPolynomial()
|
||||
|
||||
/**
|
||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
||||
* zero, degree is -1.
|
||||
*/
|
||||
override val LabeledPolynomial<C>.degree: Int
|
||||
get() = coefficients.entries.maxOfOrNull { (degs, _) -> degs.values.sum().toInt() } ?: -1
|
||||
/**
|
||||
* Map that associates variables (that appear in the polynomial in positive exponents) with their most exponents
|
||||
* in which they are appeared in the polynomial.
|
||||
*
|
||||
* As consequence all values in the map are positive integers. Also, if the polynomial is constant, the map is empty.
|
||||
* And keys of the map is the same as in [variables].
|
||||
*/
|
||||
public override val LabeledPolynomial<C>.degrees: Map<Symbol, UInt>
|
||||
get() =
|
||||
buildMap {
|
||||
coefficients.keys.forEach { degs ->
|
||||
degs.copyToBy(this, ::max)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variable].
|
||||
*/
|
||||
public override fun LabeledPolynomial<C>.degreeBy(variable: Symbol): UInt =
|
||||
coefficients.entries.maxOfOrNull { (degs, _) -> degs.getOrElse(variable) { 0u } } ?: 0u
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variables].
|
||||
*/
|
||||
public override fun LabeledPolynomial<C>.degreeBy(variables: Collection<Symbol>): UInt =
|
||||
coefficients.entries.maxOfOrNull { (degs, _) -> degs.filterKeys { it in variables }.values.sum() } ?: 0u
|
||||
/**
|
||||
* Set of all variables that appear in the polynomial in positive exponents.
|
||||
*/
|
||||
public override val LabeledPolynomial<C>.variables: Set<Symbol>
|
||||
get() =
|
||||
buildSet {
|
||||
coefficients.entries.forEach { (degs, _) -> addAll(degs.keys) }
|
||||
}
|
||||
/**
|
||||
* Count of all variables that appear in the polynomial in positive exponents.
|
||||
*/
|
||||
public override val LabeledPolynomial<C>.countOfVariables: Int get() = variables.size
|
||||
|
||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
||||
// [ListPolynomialSpace] as a context receiver
|
||||
/**
|
||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
||||
*/
|
||||
public inline fun LabeledPolynomial<C>.substitute(arguments: Map<Symbol, C>): LabeledPolynomial<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun LabeledPolynomial<C>.substitute(arguments: Map<Symbol, LabeledPolynomial<C>>) : LabeledPolynomial<C> = substitute(ring, arguments)
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
/**
|
||||
* Represents multivariate rational function that stores its numerator and denominator as [LabeledPolynomial]s.
|
||||
*/
|
||||
public data class LabeledRationalFunction<C>(
|
||||
public override val numerator: LabeledPolynomial<C>,
|
||||
public override val denominator: LabeledPolynomial<C>
|
||||
) : RationalFunction<C, LabeledPolynomial<C>> {
|
||||
override fun toString(): String = "LabeledRationalFunction${numerator.coefficients}/${denominator.coefficients}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Arithmetic context for univariate rational functions with numerator and denominator represented as [LabeledPolynomial]s.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public class LabeledRationalFunctionSpace<C, A: Ring<C>>(
|
||||
public val ring: A,
|
||||
) :
|
||||
MultivariateRationalFunctionSpaceOverMultivariatePolynomialSpace<
|
||||
C,
|
||||
Symbol,
|
||||
LabeledPolynomial<C>,
|
||||
LabeledRationalFunction<C>,
|
||||
LabeledPolynomialSpace<C, A>,
|
||||
>,
|
||||
MultivariatePolynomialSpaceOfFractions<
|
||||
C,
|
||||
Symbol,
|
||||
LabeledPolynomial<C>,
|
||||
LabeledRationalFunction<C>,
|
||||
>() {
|
||||
|
||||
/**
|
||||
* Underlying polynomial ring. Its polynomial operations are inherited by local polynomial operations.
|
||||
*/
|
||||
override val polynomialRing : LabeledPolynomialSpace<C, A> = LabeledPolynomialSpace(ring)
|
||||
/**
|
||||
* Constructor of rational functions (of type [LabeledRationalFunction]) from numerator and denominator (of type [LabeledPolynomial]).
|
||||
*/
|
||||
override fun constructRationalFunction(
|
||||
numerator: LabeledPolynomial<C>,
|
||||
denominator: LabeledPolynomial<C>
|
||||
): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction<C>(numerator, denominator)
|
||||
|
||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
||||
// [ListPolynomialSpace] as a context receiver
|
||||
/**
|
||||
* Substitutes provided constant [argument] into [this] polynomial.
|
||||
*/
|
||||
public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, C>): LabeledPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided rational function [argument] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substituteRationalFunction")
|
||||
public inline fun LabeledPolynomial<C>.substitute(argument: Map<Symbol, LabeledRationalFunction<C>>): LabeledRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided constant [argument] into [this] rational function.
|
||||
*/
|
||||
public inline fun LabeledRationalFunction<C>.substitute(argument: Map<Symbol, C>): LabeledRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] rational function.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun LabeledRationalFunction<C>.substitute(argument: Map<Symbol, LabeledPolynomial<C>>): LabeledRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided rational function [argument] into [this] rational function.
|
||||
*/
|
||||
@JvmName("substituteRationalFunction")
|
||||
public inline fun LabeledRationalFunction<C>.substitute(argument: Map<Symbol, LabeledRationalFunction<C>>): LabeledRationalFunction<C> = substitute(ring, argument)
|
||||
}
|
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.operations.ScaleOperations
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
/**
|
||||
* Represents univariate polynomial that stores its coefficients in a [List].
|
||||
*
|
||||
* @param C the type of constants.
|
||||
*/
|
||||
public data class ListPolynomial<out C>(
|
||||
/**
|
||||
* List that contains coefficients of the polynomial.
|
||||
*
|
||||
* Every monomial \(a x^d\) is stored as a coefficient \(a\) placed
|
||||
* into the list at index \(d\). For example, coefficients of a polynomial \(5 x^2 - 6\) can be represented as
|
||||
* ```
|
||||
* listOf(
|
||||
* -6, // -6 +
|
||||
* 0, // 0 x +
|
||||
* 5, // 5 x^2
|
||||
* )
|
||||
* ```
|
||||
* and also as
|
||||
* ```
|
||||
* listOf(
|
||||
* -6, // -6 +
|
||||
* 0, // 0 x +
|
||||
* 5, // 5 x^2
|
||||
* 0, // 0 x^3
|
||||
* 0, // 0 x^4
|
||||
* )
|
||||
* ```
|
||||
* It is not prohibited to put extra zeros at end of the list (as for \(0x^3\) and \(0x^4\) in the example). But the
|
||||
* longer the coefficients list the worse performance of arithmetical operations performed on it. Thus, it is
|
||||
* recommended not to put (or even to remove) extra (or useless) coefficients at the end of the coefficients list.
|
||||
* @usesMathJax
|
||||
*/
|
||||
public val coefficients: List<C>
|
||||
) {
|
||||
override fun toString(): String = "ListPolynomial$coefficients"
|
||||
}
|
||||
|
||||
/**
|
||||
* Arithmetic context for univariate polynomials with coefficients stored as a [List] constructed with the provided
|
||||
* [ring] of constants.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public open class ListPolynomialSpace<C, out A : Ring<C>>(
|
||||
public override val ring: A,
|
||||
) : PolynomialSpaceOverRing<C, ListPolynomial<C>, A> {
|
||||
/**
|
||||
* Returns sum of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.plus(other: Int): ListPolynomial<C> =
|
||||
if (other == 0) this
|
||||
else
|
||||
ListPolynomial(
|
||||
coefficients
|
||||
.toMutableList()
|
||||
.apply {
|
||||
val result = getOrElse(0) { constantZero } + other
|
||||
|
||||
if(size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns difference between the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.minus(other: Int): ListPolynomial<C> =
|
||||
if (other == 0) this
|
||||
else
|
||||
ListPolynomial(
|
||||
coefficients
|
||||
.toMutableList()
|
||||
.apply {
|
||||
val result = getOrElse(0) { constantZero } - other
|
||||
|
||||
if(size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns product of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [other] copies of [this].
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.times(other: Int): ListPolynomial<C> =
|
||||
when (other) {
|
||||
0 -> zero
|
||||
1 -> this
|
||||
else -> ListPolynomial(
|
||||
coefficients.map { it * other }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
||||
*/
|
||||
public override operator fun Int.plus(other: ListPolynomial<C>): ListPolynomial<C> =
|
||||
if (this == 0) other
|
||||
else
|
||||
ListPolynomial(
|
||||
other.coefficients
|
||||
.toMutableList()
|
||||
.apply {
|
||||
val result = this@plus + getOrElse(0) { constantZero }
|
||||
|
||||
if(size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns difference between the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
||||
*/
|
||||
public override operator fun Int.minus(other: ListPolynomial<C>): ListPolynomial<C> =
|
||||
ListPolynomial(
|
||||
other.coefficients
|
||||
.toMutableList()
|
||||
.apply {
|
||||
if (this@minus == 0) {
|
||||
indices.forEach { this[it] = -this[it] }
|
||||
} else {
|
||||
(1..lastIndex).forEach { this[it] = -this[it] }
|
||||
|
||||
val result = this@minus - getOrElse(0) { constantZero }
|
||||
|
||||
if (size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns product of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [this] copies of [other].
|
||||
*/
|
||||
public override operator fun Int.times(other: ListPolynomial<C>): ListPolynomial<C> =
|
||||
when (this) {
|
||||
0 -> zero
|
||||
1 -> other
|
||||
else -> ListPolynomial(
|
||||
other.coefficients.map { this@times * it }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public override operator fun C.plus(other: ListPolynomial<C>): ListPolynomial<C> =
|
||||
with(other.coefficients) {
|
||||
if (isEmpty()) ListPolynomial(listOf(this@plus))
|
||||
else ListPolynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
val result = if (size == 0) this@plus else this@plus + get(0)
|
||||
|
||||
if(size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public override operator fun C.minus(other: ListPolynomial<C>): ListPolynomial<C> =
|
||||
with(other.coefficients) {
|
||||
if (isEmpty()) ListPolynomial(listOf(this@minus))
|
||||
else ListPolynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
(1 .. lastIndex).forEach { this[it] = -this[it] }
|
||||
|
||||
val result = if (size == 0) this@minus else this@minus - get(0)
|
||||
|
||||
if(size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public override operator fun C.times(other: ListPolynomial<C>): ListPolynomial<C> =
|
||||
ListPolynomial(
|
||||
other.coefficients.map { this@times * it }
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.plus(other: C): ListPolynomial<C> =
|
||||
with(coefficients) {
|
||||
if (isEmpty()) ListPolynomial(listOf(other))
|
||||
else ListPolynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
val result = if (size == 0) other else get(0) + other
|
||||
|
||||
if(size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.minus(other: C): ListPolynomial<C> =
|
||||
with(coefficients) {
|
||||
if (isEmpty()) ListPolynomial(listOf(-other))
|
||||
else ListPolynomial(
|
||||
toMutableList()
|
||||
.apply {
|
||||
val result = if (size == 0) other else get(0) - other
|
||||
|
||||
if(size == 0) add(result)
|
||||
else this[0] = result
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.times(other: C): ListPolynomial<C> =
|
||||
ListPolynomial(
|
||||
coefficients.map { it * other }
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts the constant [value] to polynomial.
|
||||
*/
|
||||
public override fun number(value: C): ListPolynomial<C> = ListPolynomial(listOf(value))
|
||||
|
||||
/**
|
||||
* Returns negation of the polynomial.
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.unaryMinus(): ListPolynomial<C> =
|
||||
ListPolynomial(coefficients.map { -it })
|
||||
/**
|
||||
* Returns sum of the polynomials.
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.plus(other: ListPolynomial<C>): ListPolynomial<C> {
|
||||
val thisDegree = degree
|
||||
val otherDegree = other.degree
|
||||
return ListPolynomial(
|
||||
List(max(thisDegree, otherDegree) + 1) {
|
||||
when {
|
||||
it > thisDegree -> other.coefficients[it]
|
||||
it > otherDegree -> coefficients[it]
|
||||
else -> coefficients[it] + other.coefficients[it]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns difference of the polynomials.
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.minus(other: ListPolynomial<C>): ListPolynomial<C> {
|
||||
val thisDegree = degree
|
||||
val otherDegree = other.degree
|
||||
return ListPolynomial(
|
||||
List(max(thisDegree, otherDegree) + 1) {
|
||||
when {
|
||||
it > thisDegree -> -other.coefficients[it]
|
||||
it > otherDegree -> coefficients[it]
|
||||
else -> coefficients[it] - other.coefficients[it]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns product of the polynomials.
|
||||
*/
|
||||
public override operator fun ListPolynomial<C>.times(other: ListPolynomial<C>): ListPolynomial<C> {
|
||||
val thisDegree = degree
|
||||
val otherDegree = other.degree
|
||||
return ListPolynomial(
|
||||
List(thisDegree + otherDegree + 1) { d ->
|
||||
(max(0, d - otherDegree)..min(thisDegree, d))
|
||||
.map { coefficients[it] * other.coefficients[d - it] }
|
||||
.reduce { acc, rational -> acc + rational }
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Raises [arg] to the integer power [exponent].
|
||||
*/ // TODO: To optimize boxing
|
||||
override fun power(arg: ListPolynomial<C>, exponent: UInt): ListPolynomial<C> = super.power(arg, exponent)
|
||||
|
||||
/**
|
||||
* Instance of zero polynomial (zero of the polynomial ring).
|
||||
*/
|
||||
override val zero: ListPolynomial<C> = ListPolynomial(emptyList())
|
||||
/**
|
||||
* Instance of unit polynomial (unit of the polynomial ring).
|
||||
*/
|
||||
override val one: ListPolynomial<C> by lazy { ListPolynomial(listOf(constantOne)) }
|
||||
|
||||
/**
|
||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
||||
* zero, degree is -1.
|
||||
*/
|
||||
public override val ListPolynomial<C>.degree: Int get() = coefficients.lastIndex
|
||||
|
||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
||||
// [ListPolynomialSpace] as a context receiver
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [argument].
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.substitute(argument: C): C = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] polynomial.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.substitute(argument: ListPolynomial<C>): ListPolynomial<C> = substitute(ring, argument)
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.asFunction(): (C) -> C = asFunctionOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.asFunctionOfConstant(): (C) -> C = asFunctionOfConstantOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.asFunctionOfPolynomial(): (ListPolynomial<C>) -> ListPolynomial<C> = asFunctionOfPolynomialOver(ring)
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [argument].
|
||||
*/
|
||||
public inline operator fun ListPolynomial<C>.invoke(argument: C): C = substitute(ring, argument)
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [argument].
|
||||
*/
|
||||
public inline operator fun ListPolynomial<C>.invoke(argument: ListPolynomial<C>): ListPolynomial<C> = substitute(ring, argument)
|
||||
}
|
||||
|
||||
/**
|
||||
* Space of polynomials constructed over ring.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them as a coefficients in their terms.
|
||||
* @param A type of underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public class ScalableListPolynomialSpace<C, out A>(
|
||||
ring: A,
|
||||
) : ListPolynomialSpace<C, A>(ring), ScaleOperations<ListPolynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
|
||||
override fun scale(a: ListPolynomial<C>, value: Double): ListPolynomial<C> =
|
||||
ring { ListPolynomial(a.coefficients.map { scale(it, value) }) }
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
|
||||
|
||||
/**
|
||||
* Represents univariate rational function that stores its numerator and denominator as [ListPolynomial]s.
|
||||
*/
|
||||
public data class ListRationalFunction<C>(
|
||||
public override val numerator: ListPolynomial<C>,
|
||||
public override val denominator: ListPolynomial<C>
|
||||
) : RationalFunction<C, ListPolynomial<C>> {
|
||||
override fun toString(): String = "ListRationalFunction${numerator.coefficients}/${denominator.coefficients}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Arithmetic context for univariate rational functions with numerator and denominator represented as [ListPolynomial]s.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public class ListRationalFunctionSpace<C, A : Ring<C>> (
|
||||
public val ring: A,
|
||||
) :
|
||||
RationalFunctionSpaceOverPolynomialSpace<
|
||||
C,
|
||||
ListPolynomial<C>,
|
||||
ListRationalFunction<C>,
|
||||
ListPolynomialSpace<C, A>,
|
||||
>,
|
||||
PolynomialSpaceOfFractions<
|
||||
C,
|
||||
ListPolynomial<C>,
|
||||
ListRationalFunction<C>,
|
||||
>() {
|
||||
|
||||
/**
|
||||
* Underlying polynomial ring. Its polynomial operations are inherited by local polynomial operations.
|
||||
*/
|
||||
override val polynomialRing : ListPolynomialSpace<C, A> = ListPolynomialSpace(ring)
|
||||
/**
|
||||
* Constructor of [ListRationalFunction] from numerator and denominator [ListPolynomial].
|
||||
*/
|
||||
override fun constructRationalFunction(numerator: ListPolynomial<C>, denominator: ListPolynomial<C>): ListRationalFunction<C> =
|
||||
ListRationalFunction(numerator, denominator)
|
||||
|
||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
||||
// [ListPolynomialSpace] as a context receiver
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided argument.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.substitute(argument: C): C = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] polynomial.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.substitute(argument: ListPolynomial<C>): ListPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided rational function [argument] into [this] polynomial.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.substitute(argument: ListRationalFunction<C>): ListRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] rational function.
|
||||
*/
|
||||
public inline fun ListRationalFunction<C>.substitute(argument: ListPolynomial<C>): ListRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided rational function [argument] into [this] rational function.
|
||||
*/
|
||||
public inline fun ListRationalFunction<C>.substitute(argument: ListRationalFunction<C>): ListRationalFunction<C> = substitute(ring, argument)
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.asFunction(): (C) -> C = { substitute(ring, it) }
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.asFunctionOfConstant(): (C) -> C = { substitute(ring, it) }
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.asFunctionOfPolynomial(): (ListPolynomial<C>) -> ListPolynomial<C> = { substitute(ring, it) }
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListPolynomial<C>.asFunctionOfRationalFunction(): (ListRationalFunction<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListRationalFunction<C>.asFunctionOfPolynomial(): (ListPolynomial<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public inline fun ListRationalFunction<C>.asFunctionOfRationalFunction(): (ListRationalFunction<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided argument.
|
||||
*/
|
||||
public inline operator fun ListPolynomial<C>.invoke(argument: C): C = substitute(ring, argument)
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided argument.
|
||||
*/
|
||||
public inline operator fun ListPolynomial<C>.invoke(argument: ListPolynomial<C>): ListPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided argument.
|
||||
*/
|
||||
public inline operator fun ListPolynomial<C>.invoke(argument: ListRationalFunction<C>): ListRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Evaluates value of [this] rational function on provided argument.
|
||||
*/
|
||||
public inline operator fun ListRationalFunction<C>.invoke(argument: ListPolynomial<C>): ListRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Evaluates value of [this] rational function on provided argument.
|
||||
*/
|
||||
public inline operator fun ListRationalFunction<C>.invoke(argument: ListRationalFunction<C>): ListRationalFunction<C> = substitute(ring, argument)
|
||||
}
|
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.math.max
|
||||
|
||||
|
||||
/**
|
||||
* Represents multivariate polynomial that stores its coefficients in a [Map] and terms' signatures in a [List].
|
||||
*
|
||||
* @param C the type of constants.
|
||||
*/
|
||||
public data class NumberedPolynomial<out C>
|
||||
@PublishedApi
|
||||
internal constructor(
|
||||
/**
|
||||
* Map that contains coefficients of the polynomial.
|
||||
*
|
||||
* Every monomial \(a x_1^{d_1} ... x_n^{d_n}\) is stored as a pair "key-value" in the map, where the value is the
|
||||
* coefficient \(a\) and the key is a list that associates index of every variable in the monomial with their degree
|
||||
* in the monomial. For example, coefficients of a polynomial \(5 x_1^2 x_3^3 - 6 x_2\) can be represented as
|
||||
* ```
|
||||
* mapOf(
|
||||
* listOf(2, 0, 3) to 5, // 5 x_1^2 x_3^3 +
|
||||
* listOf(0, 1) to (-6), // (-6) x_2^1
|
||||
* )
|
||||
* ```
|
||||
* and also as
|
||||
* ```
|
||||
* mapOf(
|
||||
* listOf(2, 0, 3) to 5, // 5 x_1^2 x_3^3 +
|
||||
* listOf(0, 1) to (-6), // (-6) x_2^1
|
||||
* listOf(0, 1, 1) to 0, // 0 x_2^1 x_3^1
|
||||
* )
|
||||
* ```
|
||||
* It is not prohibited to put extra zero monomials into the map (as for \(0 x_2 x_3\) in the example). But the
|
||||
* bigger the coefficients map the worse performance of arithmetical operations performed on it. Thus, it is
|
||||
* recommended not to put (or even to remove) extra (or useless) monomials in the coefficients map.
|
||||
* @usesMathJax
|
||||
*/
|
||||
public val coefficients: Map<List<UInt>, C>
|
||||
) {
|
||||
override fun toString(): String = "NumberedPolynomial$coefficients"
|
||||
}
|
||||
|
||||
/**
|
||||
* Arithmetic context for multivariate polynomials with coefficients stored as a [Map] and terms' signatures stored as a
|
||||
* [List] constructed with the provided [ring] of constants.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public class NumberedPolynomialSpace<C, out A : Ring<C>>(
|
||||
public override val ring: A,
|
||||
) : PolynomialSpaceOverRing<C, NumberedPolynomial<C>, A> {
|
||||
/**
|
||||
* Returns sum of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
||||
*/
|
||||
public override operator fun NumberedPolynomial<C>.plus(other: Int): NumberedPolynomial<C> =
|
||||
if (other == 0) this
|
||||
else NumberedPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyList(), other.asConstant()) { it -> it + other }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
||||
*/
|
||||
public override operator fun NumberedPolynomial<C>.minus(other: Int): NumberedPolynomial<C> =
|
||||
if (other == 0) this
|
||||
else NumberedPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyList(), (-other).asConstant()) { it -> it - other }
|
||||
)
|
||||
/**
|
||||
* Returns product of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [other] copies of [this].
|
||||
*/
|
||||
public override operator fun NumberedPolynomial<C>.times(other: Int): NumberedPolynomial<C> =
|
||||
when (other) {
|
||||
0 -> zero
|
||||
1 -> this
|
||||
else -> NumberedPolynomialAsIs(
|
||||
coefficients.mapValues { it.value * other }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
||||
*/
|
||||
public override operator fun Int.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
if (this == 0) other
|
||||
else NumberedPolynomialAsIs(
|
||||
other.coefficients.withPutOrChanged(emptyList(), this@plus.asConstant()) { it -> this@plus + it }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
||||
*/
|
||||
public override operator fun Int.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
when {
|
||||
this == 0 -> -other
|
||||
other.coefficients.isEmpty() -> this.asPolynomial()
|
||||
else -> NumberedPolynomialAsIs(
|
||||
buildMap(other.coefficients.size + 1) {
|
||||
put(emptyList(), other.coefficients.computeOnOrElse(emptyList(), { this@minus.asConstant() }, { it -> this@minus - it}))
|
||||
other.coefficients.copyMapToBy(this, { _, c -> -c }) { currentC, _ -> currentC }
|
||||
}
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Returns product of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [this] copies of [other].
|
||||
*/
|
||||
public override operator fun Int.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
when (this) {
|
||||
0 -> zero
|
||||
1 -> other
|
||||
else -> NumberedPolynomialAsIs(
|
||||
other.coefficients.mapValues { this@times * it.value }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun C.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
if (other.coefficients.isEmpty()) this@plus.asPolynomial()
|
||||
else NumberedPolynomialAsIs(
|
||||
other.coefficients.withPutOrChanged(emptyList(), this@plus) { it -> this@plus + it }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun C.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
if (other.coefficients.isEmpty()) this@minus.asPolynomial()
|
||||
else NumberedPolynomialAsIs(
|
||||
buildMap(other.coefficients.size) {
|
||||
put(emptyList(), other.coefficients.computeOnOrElse(emptyList(), this@minus) { it -> this@minus - it })
|
||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, _ -> currentC })
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun C.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
other.coefficients.mapValues { this@times * it.value }
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun NumberedPolynomial<C>.plus(other: C): NumberedPolynomial<C> =
|
||||
if (coefficients.isEmpty()) other.asPolynomial()
|
||||
else NumberedPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyList(), other) { it -> it + other }
|
||||
)
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun NumberedPolynomial<C>.minus(other: C): NumberedPolynomial<C> =
|
||||
if (coefficients.isEmpty()) other.asPolynomial()
|
||||
else NumberedPolynomialAsIs(
|
||||
coefficients.withPutOrChanged(emptyList(), -other) { it -> it - other }
|
||||
)
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
override operator fun NumberedPolynomial<C>.times(other: C): NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
coefficients.mapValues { it.value * other }
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts the constant [value] to polynomial.
|
||||
*/
|
||||
public override fun number(value: C): NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to value))
|
||||
|
||||
/**
|
||||
* Returns negation of the polynomial.
|
||||
*/
|
||||
override fun NumberedPolynomial<C>.unaryMinus(): NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
coefficients.mapValues { -it.value }
|
||||
)
|
||||
/**
|
||||
* Returns sum of the polynomials.
|
||||
*/
|
||||
override operator fun NumberedPolynomial<C>.plus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
mergeBy(coefficients, other.coefficients) { c1, c2 -> c1 + c2 }
|
||||
)
|
||||
/**
|
||||
* Returns difference of the polynomials.
|
||||
*/
|
||||
override operator fun NumberedPolynomial<C>.minus(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
buildMap(coefficients.size + other.coefficients.size) {
|
||||
coefficients.copyTo(this)
|
||||
other.coefficients.copyMapToBy(this, { _, c -> -c }, { currentC, newC -> currentC - newC })
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Returns product of the polynomials.
|
||||
*/
|
||||
override operator fun NumberedPolynomial<C>.times(other: NumberedPolynomial<C>): NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
buildMap(coefficients.size * other.coefficients.size) {
|
||||
for ((degs1, c1) in coefficients) for ((degs2, c2) in other.coefficients) {
|
||||
val degs =
|
||||
(0..max(degs1.lastIndex, degs2.lastIndex))
|
||||
.map { degs1.getOrElse(it) { 0U } + degs2.getOrElse(it) { 0U } }
|
||||
val c = c1 * c2
|
||||
putOrChange(degs, c) { it -> it + c }
|
||||
}
|
||||
}
|
||||
)
|
||||
/**
|
||||
* Raises [arg] to the integer power [exponent].
|
||||
*/ // TODO: To optimize boxing
|
||||
override fun power(arg: NumberedPolynomial<C>, exponent: UInt): NumberedPolynomial<C> = super.power(arg, exponent)
|
||||
|
||||
/**
|
||||
* Instance of zero polynomial (zero of the polynomial ring).
|
||||
*/
|
||||
override val zero: NumberedPolynomial<C> = NumberedPolynomialAsIs(emptyMap())
|
||||
/**
|
||||
* Instance of unit polynomial (unit of the polynomial ring).
|
||||
*/
|
||||
override val one: NumberedPolynomial<C> by lazy { NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to constantOne)) }
|
||||
|
||||
/**
|
||||
* Maximal index (ID) of variable occurring in the polynomial with positive power. If there is no such variable,
|
||||
* the result is -1.
|
||||
*/
|
||||
public val NumberedPolynomial<C>.lastVariable: Int
|
||||
get() = coefficients.keys.maxOfOrNull { degs -> degs.lastIndex } ?: -1
|
||||
/**
|
||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
||||
* zero, degree is -1.
|
||||
*/
|
||||
override val NumberedPolynomial<C>.degree: Int
|
||||
get() = coefficients.keys.maxOfOrNull { degs -> degs.sum().toInt() } ?: -1
|
||||
/**
|
||||
* List that associates indices of variables (that appear in the polynomial in positive exponents) with their most
|
||||
* exponents in which the variables are appeared in the polynomial.
|
||||
*
|
||||
* As consequence all values in the list are non-negative integers. Also, if the polynomial is constant, the list is empty.
|
||||
* And last index of the list is [lastVariable].
|
||||
*/
|
||||
public val NumberedPolynomial<C>.degrees: List<UInt>
|
||||
get() =
|
||||
MutableList(lastVariable + 1) { 0u }.apply {
|
||||
coefficients.keys.forEach { degs ->
|
||||
degs.forEachIndexed { index, deg ->
|
||||
this[index] = max(this[index], deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variable].
|
||||
*/
|
||||
public fun NumberedPolynomial<C>.degreeBy(variable: Int): UInt =
|
||||
coefficients.keys.maxOfOrNull { degs -> degs.getOrElse(variable) { 0u } } ?: 0u
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variables].
|
||||
*/
|
||||
public fun NumberedPolynomial<C>.degreeBy(variables: Collection<Int>): UInt =
|
||||
coefficients.keys.maxOfOrNull { degs ->
|
||||
degs.withIndex().fold(0u) { acc, (index, value) -> if (index in variables) acc + value else acc }
|
||||
} ?: 0u
|
||||
/**
|
||||
* Count of variables occurring in the polynomial with positive power. If there is no such variable,
|
||||
* the result is 0.
|
||||
*/
|
||||
public val NumberedPolynomial<C>.countOfVariables: Int
|
||||
get() =
|
||||
MutableList(lastVariable + 1) { false }.apply {
|
||||
coefficients.entries.forEach { (degs, _) ->
|
||||
degs.forEachIndexed { index, deg ->
|
||||
if (deg != 0u) this[index] = true
|
||||
}
|
||||
}
|
||||
}.count { it }
|
||||
|
||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
||||
// [ListPolynomialSpace] as a context receiver
|
||||
/**
|
||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.substitute(arguments: Map<Int, C>): NumberedPolynomial<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun NumberedPolynomial<C>.substitute(arguments: Map<Int, NumberedPolynomial<C>>) : NumberedPolynomial<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.substitute(arguments: Buffer<C>): NumberedPolynomial<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun NumberedPolynomial<C>.substitute(arguments: Buffer<NumberedPolynomial<C>>) : NumberedPolynomial<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided arguments [arguments] into [this] polynomial.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.substituteFully(arguments: Buffer<C>): C = this.substituteFully(ring, arguments)
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.asFunction(): (Buffer<C>) -> C = asFunctionOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.asFunctionOfConstant(): (Buffer<C>) -> C = asFunctionOfConstantOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.asFunctionOfPolynomial(): (Buffer<NumberedPolynomial<C>>) -> NumberedPolynomial<C> = asFunctionOfPolynomialOver(ring)
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [arguments].
|
||||
*/
|
||||
public inline operator fun NumberedPolynomial<C>.invoke(arguments: Buffer<C>): C = substituteFully(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided [arguments] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("invokePolynomial")
|
||||
public inline operator fun NumberedPolynomial<C>.invoke(arguments: Buffer<NumberedPolynomial<C>>): NumberedPolynomial<C> = substitute(ring, arguments)
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.math.max
|
||||
|
||||
|
||||
/**
|
||||
* Represents multivariate rational function that stores its numerator and denominator as [NumberedPolynomial]s.
|
||||
*/
|
||||
public data class NumberedRationalFunction<C>(
|
||||
public override val numerator: NumberedPolynomial<C>,
|
||||
public override val denominator: NumberedPolynomial<C>
|
||||
) : RationalFunction<C, NumberedPolynomial<C>> {
|
||||
override fun toString(): String = "NumberedRationalFunction${numerator.coefficients}/${denominator.coefficients}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Arithmetic context for univariate rational functions with numerator and denominator represented as [NumberedPolynomial]s.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them a coefficients in their terms.
|
||||
* @param A type of provided underlying ring of constants. It's [Ring] of [C].
|
||||
* @param ring underlying ring of constants of type [A].
|
||||
*/
|
||||
public class NumberedRationalFunctionSpace<C, A: Ring<C>> (
|
||||
public val ring: A,
|
||||
) :
|
||||
RationalFunctionSpaceOverPolynomialSpace<
|
||||
C,
|
||||
NumberedPolynomial<C>,
|
||||
NumberedRationalFunction<C>,
|
||||
NumberedPolynomialSpace<C, A>,
|
||||
>,
|
||||
PolynomialSpaceOfFractions<
|
||||
C,
|
||||
NumberedPolynomial<C>,
|
||||
NumberedRationalFunction<C>,
|
||||
>() {
|
||||
|
||||
/**
|
||||
* Underlying polynomial ring. Its polynomial operations are inherited by local polynomial operations.
|
||||
*/
|
||||
public override val polynomialRing : NumberedPolynomialSpace<C, A> = NumberedPolynomialSpace(ring)
|
||||
/**
|
||||
* Constructor of rational functions (of type [NumberedRationalFunction]) from numerator and denominator (of type [NumberedPolynomial]).
|
||||
*/
|
||||
protected override fun constructRationalFunction(
|
||||
numerator: NumberedPolynomial<C>,
|
||||
denominator: NumberedPolynomial<C>
|
||||
): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction(numerator, denominator)
|
||||
|
||||
/**
|
||||
* Maximal index (ID) of variable occurring in the polynomial with positive power. If there is no such variable,
|
||||
* the result is `-1`.
|
||||
*/
|
||||
public val NumberedPolynomial<C>.lastVariable: Int get() = polynomialRing { lastVariable }
|
||||
/**
|
||||
* List that associates indices of variables (that appear in the polynomial in positive exponents) with their most
|
||||
* exponents in which the variables are appeared in the polynomial.
|
||||
*
|
||||
* As consequence all values in the list are non-negative integers. Also, if the polynomial is constant, the list is empty.
|
||||
* And last index of the list is [lastVariable].
|
||||
*/
|
||||
public val NumberedPolynomial<C>.degrees: List<UInt> get() = polynomialRing { degrees }
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variable].
|
||||
*/
|
||||
public fun NumberedPolynomial<C>.degreeBy(variable: Int): UInt = polynomialRing { degreeBy(variable) }
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variables].
|
||||
*/
|
||||
public fun NumberedPolynomial<C>.degreeBy(variables: Collection<Int>): UInt = polynomialRing { degreeBy(variables) }
|
||||
/**
|
||||
* Count of variables occurring in the polynomial with positive power. If there is no such variable,
|
||||
* the result is `0`.
|
||||
*/
|
||||
public val NumberedPolynomial<C>.countOfVariables: Int get() = polynomialRing { countOfVariables }
|
||||
|
||||
/**
|
||||
* Count of all variables that appear in the polynomial in positive exponents.
|
||||
*/
|
||||
public val NumberedRationalFunction<C>.lastVariable: Int
|
||||
get() = polynomialRing { max(numerator.lastVariable, denominator.lastVariable) }
|
||||
/**
|
||||
* Count of variables occurring in the rational function with positive power. If there is no such variable,
|
||||
* the result is `0`.
|
||||
*/
|
||||
public val NumberedRationalFunction<C>.countOfVariables: Int
|
||||
get() =
|
||||
MutableList(lastVariable + 1) { false }.apply {
|
||||
numerator.coefficients.entries.forEach { (degs, _) ->
|
||||
degs.forEachIndexed { index, deg ->
|
||||
if (deg != 0u) this[index] = true
|
||||
}
|
||||
}
|
||||
denominator.coefficients.entries.forEach { (degs, _) ->
|
||||
degs.forEachIndexed { index, deg ->
|
||||
if (deg != 0u) this[index] = true
|
||||
}
|
||||
}
|
||||
}.count { it }
|
||||
|
||||
// TODO: When context receivers will be ready move all of this substitutions and invocations to utilities with
|
||||
// [ListPolynomialSpace] as a context receiver
|
||||
/**
|
||||
* Substitutes provided constant [argument] into [this] polynomial.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.substitute(argument: Map<Int, C>): NumberedPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun NumberedPolynomial<C>.substitute(argument: Map<Int, NumberedPolynomial<C>>): NumberedPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided rational function [argument] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substituteRationalFunction")
|
||||
public inline fun NumberedPolynomial<C>.substitute(argument: Map<Int, NumberedRationalFunction<C>>): NumberedRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided constant [argument] into [this] rational function.
|
||||
*/
|
||||
public inline fun NumberedRationalFunction<C>.substitute(argument: Map<Int, C>): NumberedRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] rational function.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun NumberedRationalFunction<C>.substitute(argument: Map<Int, NumberedPolynomial<C>>): NumberedRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided rational function [argument] into [this] rational function.
|
||||
*/
|
||||
@JvmName("substituteRationalFunction")
|
||||
public inline fun NumberedRationalFunction<C>.substitute(argument: Map<Int, NumberedRationalFunction<C>>): NumberedRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided constant [argument] into [this] polynomial.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.substitute(argument: Buffer<C>): NumberedPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [argument] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun NumberedPolynomial<C>.substitute(argument: Buffer<NumberedPolynomial<C>>): NumberedPolynomial<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided rational function [argument] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("substituteRationalFunction")
|
||||
public inline fun NumberedPolynomial<C>.substitute(argument: Buffer<NumberedRationalFunction<C>>): NumberedRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided constant [argument] into [this] rational function.
|
||||
*/
|
||||
public inline fun NumberedRationalFunction<C>.substitute(argument: Buffer<C>): NumberedRationalFunction<C> = substitute(ring, argument)
|
||||
/**
|
||||
* Substitutes provided polynomial [arguments] into [this] rational function.
|
||||
*/
|
||||
@JvmName("substitutePolynomial")
|
||||
public inline fun NumberedRationalFunction<C>.substitute(arguments: Buffer<NumberedPolynomial<C>>): NumberedRationalFunction<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided rational function [arguments] into [this] rational function.
|
||||
*/
|
||||
@JvmName("substituteRationalFunction")
|
||||
public inline fun NumberedRationalFunction<C>.substitute(arguments: Buffer<NumberedRationalFunction<C>>): NumberedRationalFunction<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided constant [arguments] into [this] polynomial.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.substituteFully(arguments: Buffer<C>): C = substituteFully(ring, arguments)
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.asFunction(): (Buffer<C>) -> C = asFunctionOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.asFunctionOfConstant(): (Buffer<C>) -> C = asFunctionOfConstantOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.asFunctionOfPolynomial(): (Buffer<NumberedPolynomial<C>>) -> NumberedPolynomial<C> = asFunctionOfPolynomialOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedPolynomial<C>.asFunctionOfRationalFunction(): (Buffer<NumberedRationalFunction<C>>) -> NumberedRationalFunction<C> = asFunctionOfRationalFunctionOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedRationalFunction<C>.asFunctionOfPolynomial(): (Buffer<NumberedPolynomial<C>>) -> NumberedRationalFunction<C> = asFunctionOfPolynomialOver(ring)
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public inline fun NumberedRationalFunction<C>.asFunctionOfRationalFunction(): (Buffer<NumberedRationalFunction<C>>) -> NumberedRationalFunction<C> = asFunctionOfRationalFunctionOver(ring)
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided [arguments].
|
||||
*/
|
||||
public inline operator fun NumberedPolynomial<C>.invoke(arguments: Buffer<C>): C = substituteFully(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided [arguments] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("invokePolynomial")
|
||||
public inline operator fun NumberedPolynomial<C>.invoke(arguments: Buffer<NumberedPolynomial<C>>): NumberedPolynomial<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided [arguments] into [this] polynomial.
|
||||
*/
|
||||
@JvmName("invokeRationalFunction")
|
||||
public inline operator fun NumberedPolynomial<C>.invoke(arguments: Buffer<NumberedRationalFunction<C>>): NumberedRationalFunction<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided [arguments] into [this] rational function.
|
||||
*/
|
||||
@JvmName("invokePolynomial")
|
||||
public inline operator fun NumberedRationalFunction<C>.invoke(arguments: Buffer<NumberedPolynomial<C>>): NumberedRationalFunction<C> = substitute(ring, arguments)
|
||||
/**
|
||||
* Substitutes provided [arguments] into [this] rational function.
|
||||
*/
|
||||
@JvmName("invokeRationalFunction")
|
||||
public inline operator fun NumberedRationalFunction<C>.invoke(arguments: Buffer<NumberedRationalFunction<C>>): NumberedRationalFunction<C> = substitute(ring, arguments)
|
||||
}
|
@ -0,0 +1,527 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.js.JsName
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
/**
|
||||
* Abstraction of ring of polynomials of type [P] over ring of constants of type [C].
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them as coefficients in their terms.
|
||||
* @param P the type of polynomials.
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME", "PARAMETER_NAME_CHANGED_ON_OVERRIDE") // FIXME: Waiting for KT-31420
|
||||
public interface PolynomialSpace<C, P> : Ring<P> {
|
||||
/**
|
||||
* Returns sum of the constant and the integer represented as a constant (member of underlying ring).
|
||||
*
|
||||
* The operation is equivalent to adding [other] copies of unit of underlying ring to [this].
|
||||
*/
|
||||
@JvmName("plusConstantInt")
|
||||
public operator fun C.plus(other: Int): C
|
||||
/**
|
||||
* Returns difference between the constant and the integer represented as a constant (member of underlying ring).
|
||||
*
|
||||
* The operation is equivalent to subtraction [other] copies of unit of underlying ring from [this].
|
||||
*/
|
||||
@JvmName("minusConstantInt")
|
||||
public operator fun C.minus(other: Int): C
|
||||
/**
|
||||
* Returns product of the constant and the integer represented as a constant (member of underlying ring).
|
||||
*
|
||||
* The operation is equivalent to sum of [other] copies of [this].
|
||||
*/
|
||||
@JvmName("timesConstantInt")
|
||||
public operator fun C.times(other: Int): C
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a constant (member of underlying ring) and the constant.
|
||||
*
|
||||
* The operation is equivalent to adding [this] copies of unit of underlying ring to [other].
|
||||
*/
|
||||
@JvmName("plusIntConstant")
|
||||
public operator fun Int.plus(other: C): C
|
||||
/**
|
||||
* Returns difference between the integer represented as a constant (member of underlying ring) and the constant.
|
||||
*
|
||||
* The operation is equivalent to subtraction [this] copies of unit of underlying ring from [other].
|
||||
*/
|
||||
@JvmName("minusIntConstant")
|
||||
public operator fun Int.minus(other: C): C
|
||||
/**
|
||||
* Returns product of the integer represented as a constant (member of underlying ring) and the constant.
|
||||
*
|
||||
* The operation is equivalent to sum of [this] copies of [other].
|
||||
*/
|
||||
@JvmName("timesIntConstant")
|
||||
public operator fun Int.times(other: C): C
|
||||
|
||||
/**
|
||||
* Converts the integer [value] to constant.
|
||||
*/
|
||||
public fun constantNumber(value: Int): C = constantOne * value
|
||||
/**
|
||||
* Converts the integer to constant.
|
||||
*/
|
||||
public fun Int.asConstant(): C = constantNumber(this)
|
||||
|
||||
/**
|
||||
* Returns sum of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [other] copies of unit polynomial to [this].
|
||||
*/
|
||||
public operator fun P.plus(other: Int): P = addMultipliedByDoubling(this, one, other)
|
||||
/**
|
||||
* Returns difference between the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [other] copies of unit polynomial from [this].
|
||||
*/
|
||||
public operator fun P.minus(other: Int): P = addMultipliedByDoubling(this, one, -other)
|
||||
/**
|
||||
* Returns product of the polynomial and the integer represented as a polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [other] copies of [this].
|
||||
*/
|
||||
public operator fun P.times(other: Int): P = multiplyByDoubling(this, other)
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to adding [this] copies of unit polynomial to [other].
|
||||
*/
|
||||
public operator fun Int.plus(other: P): P = addMultipliedByDoubling(other, one, this)
|
||||
/**
|
||||
* Returns difference between the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to subtraction [this] copies of unit polynomial from [other].
|
||||
*/
|
||||
public operator fun Int.minus(other: P): P = addMultipliedByDoubling(-other, one, this)
|
||||
/**
|
||||
* Returns product of the integer represented as a polynomial and the polynomial.
|
||||
*
|
||||
* The operation is equivalent to sum of [this] copies of [other].
|
||||
*/
|
||||
public operator fun Int.times(other: P): P = multiplyByDoubling(other, this)
|
||||
|
||||
/**
|
||||
* Converts the integer [value] to polynomial.
|
||||
*/
|
||||
public fun number(value: Int): P = number(constantNumber(value))
|
||||
/**
|
||||
* Converts the integer to polynomial.
|
||||
*/
|
||||
public fun Int.asPolynomial(): P = number(this)
|
||||
|
||||
/**
|
||||
* Returns the same constant.
|
||||
*/
|
||||
@JvmName("unaryPlusConstant")
|
||||
@JsName("unaryPlusConstant")
|
||||
public operator fun C.unaryPlus(): C = this
|
||||
/**
|
||||
* Returns negation of the constant.
|
||||
*/
|
||||
@JvmName("unaryMinusConstant")
|
||||
@JsName("unaryMinusConstant")
|
||||
public operator fun C.unaryMinus(): C
|
||||
/**
|
||||
* Returns sum of the constants.
|
||||
*/
|
||||
@JvmName("plusConstantConstant")
|
||||
@JsName("plusConstantConstant")
|
||||
public operator fun C.plus(other: C): C
|
||||
/**
|
||||
* Returns difference of the constants.
|
||||
*/
|
||||
@JvmName("minusConstantConstant")
|
||||
@JsName("minusConstantConstant")
|
||||
public operator fun C.minus(other: C): C
|
||||
/**
|
||||
* Returns product of the constants.
|
||||
*/
|
||||
@JvmName("timesConstantConstant")
|
||||
@JsName("timesConstantConstant")
|
||||
public operator fun C.times(other: C): C
|
||||
/**
|
||||
* Raises [arg] to the integer power [exponent].
|
||||
*/
|
||||
@JvmName("powerConstant")
|
||||
@JsName("powerConstant")
|
||||
public fun power(arg: C, exponent: UInt) : C
|
||||
|
||||
/**
|
||||
* Instance of zero constant (zero of the underlying ring).
|
||||
*/
|
||||
public val constantZero: C
|
||||
/**
|
||||
* Instance of unit constant (unit of the underlying ring).
|
||||
*/
|
||||
public val constantOne: C
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
@JvmName("plusConstantPolynomial")
|
||||
public operator fun C.plus(other: P): P
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
@JvmName("minusConstantPolynomial")
|
||||
public operator fun C.minus(other: P): P
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
@JvmName("timesConstantPolynomial")
|
||||
public operator fun C.times(other: P): P
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
@JvmName("plusPolynomialConstant")
|
||||
public operator fun P.plus(other: C): P
|
||||
/**
|
||||
* Returns difference between the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
@JvmName("minusPolynomialConstant")
|
||||
public operator fun P.minus(other: C): P
|
||||
/**
|
||||
* Returns product of the constant represented as a polynomial and the polynomial.
|
||||
*/
|
||||
@JvmName("timesPolynomialConstant")
|
||||
public operator fun P.times(other: C): P
|
||||
|
||||
/**
|
||||
* Converts the constant [value] to polynomial.
|
||||
*/
|
||||
public fun number(value: C): P = one * value
|
||||
/**
|
||||
* Converts the constant to polynomial.
|
||||
*/
|
||||
public fun C.asPolynomial(): P = number(this)
|
||||
|
||||
/**
|
||||
* Returns the same polynomial.
|
||||
*/
|
||||
public override operator fun P.unaryPlus(): P = this
|
||||
/**
|
||||
* Returns negation of the polynomial.
|
||||
*/
|
||||
public override operator fun P.unaryMinus(): P
|
||||
/**
|
||||
* Returns sum of the polynomials.
|
||||
*/
|
||||
public override operator fun P.plus(other: P): P
|
||||
/**
|
||||
* Returns difference of the polynomials.
|
||||
*/
|
||||
public override operator fun P.minus(other: P): P
|
||||
/**
|
||||
* Returns product of the polynomials.
|
||||
*/
|
||||
public override operator fun P.times(other: P): P
|
||||
/**
|
||||
* Raises [arg] to the integer power [exponent].
|
||||
*/
|
||||
public override fun power(arg: P, exponent: UInt) : P = exponentiateBySquaring(arg, exponent)
|
||||
|
||||
/**
|
||||
* Instance of zero polynomial (zero of the polynomial ring).
|
||||
*/
|
||||
public override val zero: P
|
||||
/**
|
||||
* Instance of unit polynomial (unit of the polynomial ring).
|
||||
*/
|
||||
public override val one: P
|
||||
|
||||
/**
|
||||
* Degree of the polynomial, [see also](https://en.wikipedia.org/wiki/Degree_of_a_polynomial). If the polynomial is
|
||||
* zero, degree is -1.
|
||||
*/
|
||||
public val P.degree: Int
|
||||
|
||||
override fun add(left: P, right: P): P = left + right
|
||||
override fun multiply(left: P, right: P): P = left * right
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of ring of polynomials of type [P] over ring of constants of type [C]. It also assumes that there is
|
||||
* provided [ring] (of type [A]), that provides constant-wise operations.
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them as coefficients in their terms.
|
||||
* @param P the type of polynomials.
|
||||
* @param A the type of algebraic structure (precisely, of ring) provided for constants.
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME") // FIXME: Waiting for KT-31420
|
||||
public interface PolynomialSpaceOverRing<C, P, out A: Ring<C>> : PolynomialSpace<C, P> {
|
||||
|
||||
/**
|
||||
* Underlying ring of constants. Its operations on constants are inherited by local operations on constants.
|
||||
*/
|
||||
public val ring: A
|
||||
|
||||
/**
|
||||
* Returns sum of the constant and the integer represented as a constant (member of underlying ring).
|
||||
*
|
||||
* The operation is equivalent to adding [other] copies of unit of underlying ring to [this].
|
||||
*/
|
||||
@JvmName("plusConstantInt")
|
||||
public override operator fun C.plus(other: Int): C = ring { addMultipliedByDoubling(this@plus, one, other) }
|
||||
/**
|
||||
* Returns difference between the constant and the integer represented as a constant (member of underlying ring).
|
||||
*
|
||||
* The operation is equivalent to subtraction [other] copies of unit of underlying ring from [this].
|
||||
*/
|
||||
@JvmName("minusConstantInt")
|
||||
public override operator fun C.minus(other: Int): C = ring { addMultipliedByDoubling(this@minus, one, -other) }
|
||||
/**
|
||||
* Returns product of the constant and the integer represented as a constant (member of underlying ring).
|
||||
*
|
||||
* The operation is equivalent to sum of [other] copies of [this].
|
||||
*/
|
||||
@JvmName("timesConstantInt")
|
||||
public override operator fun C.times(other: Int): C = ring { multiplyByDoubling(this@times, other) }
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a constant (member of underlying ring) and the constant.
|
||||
*
|
||||
* The operation is equivalent to adding [this] copies of unit of underlying ring to [other].
|
||||
*/
|
||||
@JvmName("plusIntConstant")
|
||||
public override operator fun Int.plus(other: C): C = ring { addMultipliedByDoubling(other, one, this@plus) }
|
||||
/**
|
||||
* Returns difference between the integer represented as a constant (member of underlying ring) and the constant.
|
||||
*
|
||||
* The operation is equivalent to subtraction [this] copies of unit of underlying ring from [other].
|
||||
*/
|
||||
@JvmName("minusIntConstant")
|
||||
public override operator fun Int.minus(other: C): C = ring { addMultipliedByDoubling(-other, one, this@minus) }
|
||||
/**
|
||||
* Returns product of the integer represented as a constant (member of underlying ring) and the constant.
|
||||
*
|
||||
* The operation is equivalent to sum of [this] copies of [other].
|
||||
*/
|
||||
@JvmName("timesIntConstant")
|
||||
public override operator fun Int.times(other: C): C = ring { multiplyByDoubling(other, this@times) }
|
||||
|
||||
/**
|
||||
* Returns negation of the constant.
|
||||
*/
|
||||
@JvmName("unaryMinusConstant")
|
||||
public override operator fun C.unaryMinus(): C = ring { -this@unaryMinus }
|
||||
/**
|
||||
* Returns sum of the constants.
|
||||
*/
|
||||
@JvmName("plusConstantConstant")
|
||||
public override operator fun C.plus(other: C): C = ring { this@plus + other }
|
||||
/**
|
||||
* Returns difference of the constants.
|
||||
*/
|
||||
@JvmName("minusConstantConstant")
|
||||
public override operator fun C.minus(other: C): C = ring { this@minus - other }
|
||||
/**
|
||||
* Returns product of the constants.
|
||||
*/
|
||||
@JvmName("timesConstantConstant")
|
||||
public override operator fun C.times(other: C): C = ring { this@times * other }
|
||||
/**
|
||||
* Raises [arg] to the integer power [exponent].
|
||||
*/
|
||||
@JvmName("powerConstant")
|
||||
override fun power(arg: C, exponent: UInt): C = ring { power(arg, exponent) }
|
||||
|
||||
/**
|
||||
* Instance of zero constant (zero of the underlying ring).
|
||||
*/
|
||||
public override val constantZero: C get() = ring.zero
|
||||
/**
|
||||
* Instance of unit constant (unit of the underlying ring).
|
||||
*/
|
||||
public override val constantOne: C get() = ring.one
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of ring of polynomials of type [P] of variables of type [V] and over ring of constants of type [C].
|
||||
*
|
||||
* @param C the type of constants. Polynomials have them as coefficients in their terms.
|
||||
* @param V the type of variables. Polynomials have them in representations of terms.
|
||||
* @param P the type of polynomials.
|
||||
*/
|
||||
@Suppress("INAPPLICABLE_JVM_NAME") // FIXME: Waiting for KT-31420
|
||||
public interface MultivariatePolynomialSpace<C, V, P>: PolynomialSpace<C, P> {
|
||||
/**
|
||||
* Returns sum of the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
||||
*/
|
||||
@JvmName("plusVariableInt")
|
||||
@JsName("plusVariableInt")
|
||||
public operator fun V.plus(other: Int): P
|
||||
/**
|
||||
* Returns difference between the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
||||
*/
|
||||
@JvmName("minusVariableInt")
|
||||
@JsName("minusVariableInt")
|
||||
public operator fun V.minus(other: Int): P
|
||||
/**
|
||||
* Returns product of the variable represented as a monic monomial and the integer represented as a constant polynomial.
|
||||
*/
|
||||
@JvmName("timesVariableInt")
|
||||
@JsName("timesVariableInt")
|
||||
public operator fun V.times(other: Int): P
|
||||
|
||||
/**
|
||||
* Returns sum of the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("plusIntVariable")
|
||||
@JsName("plusIntVariable")
|
||||
public operator fun Int.plus(other: V): P
|
||||
/**
|
||||
* Returns difference between the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("minusIntVariable")
|
||||
@JsName("minusIntVariable")
|
||||
public operator fun Int.minus(other: V): P
|
||||
/**
|
||||
* Returns product of the integer represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("timesIntVariable")
|
||||
@JsName("timesIntVariable")
|
||||
public operator fun Int.times(other: V): P
|
||||
|
||||
/**
|
||||
* Returns sum of the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
||||
*/
|
||||
@JvmName("plusVariableConstant")
|
||||
@JsName("plusVariableConstant")
|
||||
public operator fun V.plus(other: C): P
|
||||
/**
|
||||
* Returns difference between the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
||||
*/
|
||||
@JvmName("minusVariableConstant")
|
||||
@JsName("minusVariableConstant")
|
||||
public operator fun V.minus(other: C): P
|
||||
/**
|
||||
* Returns product of the variable represented as a monic monomial and the constant represented as a constant polynomial.
|
||||
*/
|
||||
@JvmName("timesVariableConstant")
|
||||
@JsName("timesVariableConstant")
|
||||
public operator fun V.times(other: C): P
|
||||
|
||||
/**
|
||||
* Returns sum of the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("plusConstantVariable")
|
||||
@JsName("plusConstantVariable")
|
||||
public operator fun C.plus(other: V): P
|
||||
/**
|
||||
* Returns difference between the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("minusConstantVariable")
|
||||
@JsName("minusConstantVariable")
|
||||
public operator fun C.minus(other: V): P
|
||||
/**
|
||||
* Returns product of the constant represented as a constant polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("timesConstantVariable")
|
||||
@JsName("timesConstantVariable")
|
||||
public operator fun C.times(other: V): P
|
||||
|
||||
/**
|
||||
* Represents the variable as a monic monomial.
|
||||
*/
|
||||
@JvmName("unaryPlusVariable")
|
||||
public operator fun V.unaryPlus(): P
|
||||
/**
|
||||
* Returns negation of representation of the variable as a monic monomial.
|
||||
*/
|
||||
@JvmName("unaryMinusVariable")
|
||||
public operator fun V.unaryMinus(): P
|
||||
/**
|
||||
* Returns sum of the variables represented as monic monomials.
|
||||
*/
|
||||
@JvmName("plusVariableVariable")
|
||||
public operator fun V.plus(other: V): P
|
||||
/**
|
||||
* Returns difference between the variables represented as monic monomials.
|
||||
*/
|
||||
@JvmName("minusVariableVariable")
|
||||
public operator fun V.minus(other: V): P
|
||||
/**
|
||||
* Returns product of the variables represented as monic monomials.
|
||||
*/
|
||||
@JvmName("timesVariableVariable")
|
||||
public operator fun V.times(other: V): P
|
||||
|
||||
/**
|
||||
* Represents the [variable] as a monic monomial.
|
||||
*/
|
||||
@JvmName("numberVariable")
|
||||
public fun number(variable: V): P = +variable
|
||||
/**
|
||||
* Represents the variable as a monic monomial.
|
||||
*/
|
||||
@JvmName("asPolynomialVariable")
|
||||
public fun V.asPolynomial(): P = number(this)
|
||||
|
||||
/**
|
||||
* Returns sum of the variable represented as a monic monomial and the polynomial.
|
||||
*/
|
||||
@JvmName("plusVariablePolynomial")
|
||||
public operator fun V.plus(other: P): P
|
||||
/**
|
||||
* Returns difference between the variable represented as a monic monomial and the polynomial.
|
||||
*/
|
||||
@JvmName("minusVariablePolynomial")
|
||||
public operator fun V.minus(other: P): P
|
||||
/**
|
||||
* Returns product of the variable represented as a monic monomial and the polynomial.
|
||||
*/
|
||||
@JvmName("timesVariablePolynomial")
|
||||
public operator fun V.times(other: P): P
|
||||
|
||||
/**
|
||||
* Returns sum of the polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("plusPolynomialVariable")
|
||||
public operator fun P.plus(other: V): P
|
||||
/**
|
||||
* Returns difference between the polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("minusPolynomialVariable")
|
||||
public operator fun P.minus(other: V): P
|
||||
/**
|
||||
* Returns product of the polynomial and the variable represented as a monic monomial.
|
||||
*/
|
||||
@JvmName("timesPolynomialVariable")
|
||||
public operator fun P.times(other: V): P
|
||||
|
||||
/**
|
||||
* Map that associates variables (that appear in the polynomial in positive exponents) with their most exponents
|
||||
* in which they are appeared in the polynomial.
|
||||
*
|
||||
* As consequence all values in the map are positive integers. Also, if the polynomial is constant, the map is empty.
|
||||
* And keys of the map is the same as in [variables].
|
||||
*/
|
||||
public val P.degrees: Map<V, UInt>
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variable].
|
||||
*/
|
||||
public fun P.degreeBy(variable: V): UInt = degrees.getOrElse(variable) { 0u }
|
||||
/**
|
||||
* Counts degree of the polynomial by the specified [variables].
|
||||
*/
|
||||
public fun P.degreeBy(variables: Collection<V>): UInt
|
||||
/**
|
||||
* Set of all variables that appear in the polynomial in positive exponents.
|
||||
*/
|
||||
public val P.variables: Set<V> get() = degrees.keys
|
||||
/**
|
||||
* Count of all variables that appear in the polynomial in positive exponents.
|
||||
*/
|
||||
public val P.countOfVariables: Int get() = variables.size
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.operations.*
|
||||
|
||||
|
||||
// TODO: All of this should be moved to algebraic structures' place for utilities
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Returns product of [arg] and integer [multiplier].
|
||||
*
|
||||
* @param arg the multiplicand.
|
||||
* @param multiplier the integer multiplier.
|
||||
* @return product of the multiplicand [arg] and the multiplier [multiplier].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal fun <C> Group<C>.multiplyByDoubling(arg: C, multiplier: Int): C =
|
||||
if (multiplier >= 0) multiplyByDoubling(arg, multiplier.toUInt())
|
||||
else multiplyByDoubling(-arg, (-multiplier).toUInt())
|
||||
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Adds product of [arg] and [multiplier] to [base].
|
||||
*
|
||||
* @param base the augend.
|
||||
* @param arg the multiplicand.
|
||||
* @param multiplier the integer multiplier.
|
||||
* @return sum of the augend [base] and product of the multiplicand [arg] and the multiplier [multiplier].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal fun <C> GroupOps<C>.addMultipliedByDoubling(base: C, arg: C, multiplier: Int): C =
|
||||
if (multiplier >= 0) addMultipliedByDoubling(base, arg, multiplier.toUInt())
|
||||
else addMultipliedByDoubling(base, -arg, (-multiplier).toUInt())
|
||||
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Returns product of [arg] and integer [multiplier].
|
||||
*
|
||||
* This is implementation of variation of [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring)
|
||||
*
|
||||
* @param arg the multiplicand.
|
||||
* @param multiplier the integer multiplier.
|
||||
* @return product of the multiplicand [arg] and the multiplier [multiplier].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal tailrec fun <C> Group<C>.multiplyByDoubling(arg: C, multiplier: UInt): C =
|
||||
when {
|
||||
multiplier == 0u -> zero
|
||||
multiplier == 1u -> arg
|
||||
multiplier and 1u == 0u -> multiplyByDoubling(arg + arg, multiplier shr 1)
|
||||
multiplier and 1u == 1u -> addMultipliedByDoubling(arg, arg + arg, multiplier shr 1)
|
||||
else -> error("Error in multiplication group instant by unsigned integer: got reminder by division by 2 different from 0 and 1")
|
||||
}
|
||||
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Adds product of [arg] and [multiplier] to [base].
|
||||
*
|
||||
* This is implementation of variation of [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring)
|
||||
*
|
||||
* @param base the augend.
|
||||
* @param arg the multiplicand.
|
||||
* @param multiplier the integer multiplier.
|
||||
* @return sum of the augend [base] and product of the multiplicand [arg] and the multiplier [multiplier].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal tailrec fun <C> GroupOps<C>.addMultipliedByDoubling(base: C, arg: C, multiplier: UInt): C =
|
||||
when {
|
||||
multiplier == 0u -> base
|
||||
multiplier == 1u -> base + arg
|
||||
multiplier and 1u == 0u -> addMultipliedByDoubling(base, arg + arg, multiplier shr 1)
|
||||
multiplier and 1u == 1u -> addMultipliedByDoubling(base + arg, arg + arg, multiplier shr 1)
|
||||
else -> error("Error in multiplication group instant by unsigned integer: got reminder by division by 2 different from 0 and 1")
|
||||
}
|
||||
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Raises [arg] to the integer power [exponent].
|
||||
*
|
||||
* @param arg the base of the power.
|
||||
* @param exponent the exponent of the power.
|
||||
* @return [arg] raised to the power [exponent].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal fun <C> Field<C>.exponentiateBySquaring(arg: C, exponent: Int): C =
|
||||
if (exponent >= 0) exponentiateBySquaring(arg, exponent.toUInt())
|
||||
else exponentiateBySquaring(one / arg, (-exponent).toUInt())
|
||||
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Multiplies [base] and [arg] raised to the integer power [exponent].
|
||||
*
|
||||
* @param base the multiplicand.
|
||||
* @param arg the base of the power.
|
||||
* @param exponent the exponent of the power.
|
||||
* @return product of [base] and [arg] raised to the power [exponent].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal fun <C> Field<C>.multiplyExponentiatedBySquaring(base: C, arg: C, exponent: Int): C =
|
||||
if (exponent >= 0) multiplyExponentiatedBySquaring(base, arg, exponent.toUInt())
|
||||
else multiplyExponentiatedBySquaring(base, one / arg, (-exponent).toUInt())
|
||||
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Raises [arg] to the integer power [exponent].
|
||||
*
|
||||
* This is implementation of variation of [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring)
|
||||
*
|
||||
* @param arg the base of the power.
|
||||
* @param exponent the exponent of the power.
|
||||
* @return [arg] raised to the power [exponent].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal tailrec fun <C> Ring<C>.exponentiateBySquaring(arg: C, exponent: UInt): C =
|
||||
when {
|
||||
exponent == 0u -> zero
|
||||
exponent == 1u -> arg
|
||||
exponent and 1u == 0u -> exponentiateBySquaring(arg * arg, exponent shr 1)
|
||||
exponent and 1u == 1u -> multiplyExponentiatedBySquaring(arg, arg * arg, exponent shr 1)
|
||||
else -> error("Error in multiplication group instant by unsigned integer: got reminder by division by 2 different from 0 and 1")
|
||||
}
|
||||
|
||||
// FIXME: Move receiver to context receiver
|
||||
/**
|
||||
* Multiplies [base] and [arg] raised to the integer power [exponent].
|
||||
*
|
||||
* This is implementation of variation of [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring)
|
||||
*
|
||||
* @param base the multiplicand.
|
||||
* @param arg the base of the power.
|
||||
* @param exponent the exponent of the power.
|
||||
* @return product of [base] and [arg] raised to the power [exponent].
|
||||
* @author Gleb Minaev
|
||||
*/
|
||||
internal tailrec fun <C> RingOps<C>.multiplyExponentiatedBySquaring(base: C, arg: C, exponent: UInt): C =
|
||||
when {
|
||||
exponent == 0u -> base
|
||||
exponent == 1u -> base * arg
|
||||
exponent and 1u == 0u -> multiplyExponentiatedBySquaring(base, arg * arg, exponent shr 1)
|
||||
exponent and 1u == 1u -> multiplyExponentiatedBySquaring(base * arg, arg * arg, exponent shr 1)
|
||||
else -> error("Error in multiplication group instant by unsigned integer: got reminder by division by 2 different from 0 and 1")
|
||||
}
|
@ -0,0 +1,906 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import kotlin.contracts.InvocationKind.*
|
||||
import kotlin.contracts.contract
|
||||
|
||||
|
||||
/**
|
||||
* Computes the given lambda [compute] on value corresponding to the provided [key] or `null` if the key is not present.
|
||||
*
|
||||
* @param key key which corresponding value will be used if it's present.
|
||||
* @param compute lambda that is computed on the received value.
|
||||
* @return result of the computation of the lambda.
|
||||
*/
|
||||
internal inline fun <K, V, R> Map<in K, V>.computeOn(key: K, compute: (V?) -> R): R {
|
||||
contract {
|
||||
callsInPlace(compute, EXACTLY_ONCE)
|
||||
}
|
||||
return compute(get(key))
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the given lambda [compute] on value corresponding to the provided [key] or computes the given lambda
|
||||
* [defaultResult] if the key is not present.
|
||||
*
|
||||
* @param key key which corresponding value will be used if it's present.
|
||||
* @param compute lambda that is computed on the value corresponding to the [key].
|
||||
* @param defaultResult lambda that is computed if the [key] is not present.
|
||||
* @return result of [compute] lambda if the [key] is present or result of [defaultResult] otherwise.
|
||||
*/
|
||||
internal inline fun <K, V, R> Map<K, V>.computeOnOrElse(key: K, defaultResult: () -> R, compute: (value: V) -> R): R {
|
||||
contract {
|
||||
callsInPlace(defaultResult, AT_MOST_ONCE)
|
||||
callsInPlace(compute, AT_MOST_ONCE)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return (if (key !in this) defaultResult() else compute(get(key) as V))
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the given lambda [compute] on value corresponding to the provided [key] or computes the given lambda
|
||||
* [defaultResult] if the key is not present.
|
||||
*
|
||||
* @param key key which corresponding value will be used if it's present.
|
||||
* @param compute lambda that is computed on the value corresponding to the [key].
|
||||
* @param defaultResult default result that is returned in case of the [key]'s absence.
|
||||
* @return result of [compute] lambda if the [key] is present or [defaultResult] otherwise.
|
||||
*/
|
||||
internal inline fun <K, V, R> Map<K, V>.computeOnOrElse(key: K, defaultResult: R, compute: (value: V) -> R): R {
|
||||
contract {
|
||||
callsInPlace(compute, AT_MOST_ONCE)
|
||||
}
|
||||
return computeOnOrElse(key, { defaultResult }, compute)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the given lambda [compute] on value corresponding to the provided [key] or computes the given lambda
|
||||
* [defaultResult] if the key is not present.
|
||||
*
|
||||
* @param key key which corresponding value will be used if it's present.
|
||||
* @param compute lambda that is computed on the value corresponding to the [key].
|
||||
* @param defaultResult default result that is returned in case of the [key]'s absence.
|
||||
* @return result of [compute] lambda if the [key] is present or [defaultResult] otherwise.
|
||||
*/
|
||||
internal inline fun <K, V, R> Map<K, V>.computeOnOrElse(key: K, defaultResult: R, compute: (key: K, value: V) -> R): R {
|
||||
contract {
|
||||
callsInPlace(compute, AT_MOST_ONCE)
|
||||
}
|
||||
return computeOnOrElse(key, { defaultResult }, { it -> compute(key, it) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the [transformation][transform] to the value corresponding to the given [key] or null instead if it's not
|
||||
* present.
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param transform transformation to apply.
|
||||
* @return result of the transformation
|
||||
*/
|
||||
internal inline fun <K, V> MutableMap<in K, V>.applyToKey(key: K, transform: (currentValue: V?) -> V): V {
|
||||
contract {
|
||||
callsInPlace(transform, EXACTLY_ONCE)
|
||||
}
|
||||
return computeOn(key, transform).also { this[key] = it }
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on presence of value corresponding to the given [key] either puts new value calculated by [valueOnPut] or
|
||||
* changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut lazily calculated value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* current value as a parameter.
|
||||
* @return result value corresponding to the [key].
|
||||
*/
|
||||
internal inline fun <K, V> MutableMap<K, V>.putOrChange(key: K, valueOnPut: () -> V, transformOnChange: (currentValue: V) -> V): V {
|
||||
contract {
|
||||
callsInPlace(valueOnPut, AT_MOST_ONCE)
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return computeOnOrElse(key, valueOnPut, transformOnChange).also { this[key] = it }
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on presence of value corresponding to the given [key] either puts new value [valueOnPut] or
|
||||
* changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* current value as a parameter.
|
||||
* @return result value corresponding to the [key].
|
||||
*/
|
||||
internal inline fun <K, V> MutableMap<K, V>.putOrChange(key: K, valueOnPut: V, transformOnChange: (currentValue: V) -> V): V {
|
||||
contract {
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return putOrChange<K, V>(key, { valueOnPut }, transformOnChange)
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on presence of value corresponding to the given [key] either puts new value [valueOnPut] or
|
||||
* changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* current value and new value as parameters.
|
||||
* @return result value corresponding to the [key].
|
||||
*/
|
||||
internal inline fun <K, V> MutableMap<K, V>.putOrChange(key: K, valueOnPut: V, transformOnChange: (currentValue: V, newValue: V) -> V): V {
|
||||
contract {
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return putOrChange<K, V>(key, { valueOnPut }, { transformOnChange(it, valueOnPut) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on presence of value corresponding to the given [key] either puts new value [valueOnPut] or
|
||||
* changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* the [key], current value, and new value as parameters.
|
||||
* @return result value corresponding to the [key].
|
||||
*/
|
||||
internal inline fun <K, V> MutableMap<K, V>.putOrChange(key: K, valueOnPut: V, transformOnChange: (key: K, currentValue: V, newValue: V) -> V): V {
|
||||
contract {
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return putOrChange<K, V>(key, { valueOnPut }, { transformOnChange(key, it, valueOnPut) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates copy of [the map][this] and applies the [transformation][transform] to the value corresponding to the given
|
||||
* [key] in the copy or null instead if it's not present.
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param transform transformation to apply.
|
||||
* @return the copy of [the map][this].
|
||||
*/
|
||||
internal inline fun <K, V> Map<in K, V>.withAppliedToKey(key: K, transform: (currentValue: V?) -> V): Map<K, V> {
|
||||
contract {
|
||||
callsInPlace(transform, EXACTLY_ONCE)
|
||||
}
|
||||
return buildMap(size) {
|
||||
putAll(this)
|
||||
applyToKey(key, transform)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new
|
||||
* value calculated by [valueOnPut] or changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut lazily calculated value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* current value as a parameter.
|
||||
* @return the copy of [the map][this].
|
||||
*/
|
||||
internal inline fun <K, V> Map<K, V>.withPutOrChanged(key: K, valueOnPut: () -> V, transformOnChange: (currentValue: V) -> V): Map<K, V> {
|
||||
contract {
|
||||
callsInPlace(valueOnPut, AT_MOST_ONCE)
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return buildMap(size + 1) {
|
||||
putAll(this@withPutOrChanged)
|
||||
putOrChange(key, valueOnPut, transformOnChange)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new
|
||||
* value [valueOnPut] or changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* current value as a parameter.
|
||||
* @return the copy of [the map][this].
|
||||
*/
|
||||
internal inline fun <K, V> Map<K, V>.withPutOrChanged(key: K, valueOnPut: V, transformOnChange: (currentValue: V) -> V): Map<K, V> {
|
||||
contract {
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return withPutOrChanged<K, V>(key, { valueOnPut }, transformOnChange)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new
|
||||
* value [valueOnPut] or changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* current value and new value as parameters.
|
||||
* @return the copy of [the map][this].
|
||||
*/
|
||||
internal inline fun <K, V> Map<K, V>.withPutOrChanged(key: K, valueOnPut: V, transformOnChange: (currentValue: V, newValue: V) -> V): Map<K, V> {
|
||||
contract {
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return withPutOrChanged<K, V>(key, { valueOnPut }, { transformOnChange(it, valueOnPut) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates copy of [the map][this] and depending on presence of value corresponding to the given [key] either puts new
|
||||
* value [valueOnPut] or changes the present value with [transformOnChange].
|
||||
*
|
||||
* @param key key to check.
|
||||
* @param valueOnPut value to put in case of absence of the [key].
|
||||
* @param transformOnChange transform to apply to current value corresponding to the [key] in case of its presence. Uses
|
||||
* the [key], current value, and new value as parameters.
|
||||
* @return the copy of [the map][this].
|
||||
*/
|
||||
internal inline fun <K, V> Map<K, V>.withPutOrChanged(key: K, valueOnPut: V, transformOnChange: (key: K, currentValue: V, newValue: V) -> V): Map<K, V> {
|
||||
contract {
|
||||
callsInPlace(transformOnChange, AT_MOST_ONCE)
|
||||
}
|
||||
return withPutOrChanged<K, V>(key, { valueOnPut }, { transformOnChange(key, it, valueOnPut) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies entries of [this map][this] to the [destination] map overriding present ones if needed.
|
||||
*
|
||||
* @receiver map to be copied.
|
||||
* @param destination map to receive copies.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal fun <K, V, D: MutableMap<K, V>> Map<K, V>.copyTo(destination: D): D {
|
||||
for ((key, value) in this) {
|
||||
destination[key] = value
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies entries of [this map][this] to the [destination] map merging present entries with new ones using [resolve]
|
||||
* lambda.
|
||||
*
|
||||
* @receiver map to be copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param resolve lambda function that resolves overriding. It takes a key, current value corresponding to the key, and
|
||||
* a new one and returns value to associate to the key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V: W, W, D: MutableMap<K, W>> Map<out K, V>.copyToBy(destination: D, resolve: (key: K, currentValue: W, newValue: V) -> W): D {
|
||||
for ((key, value) in this) {
|
||||
destination.putOrChange(key, value) { it -> resolve(key, it, value) }
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies entries of [this map][this] to the [destination] map merging present entries with new ones using [resolve]
|
||||
* lambda.
|
||||
*
|
||||
* @receiver map to be copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param resolve lambda function that resolves overriding. It takes current value corresponding to some key, and
|
||||
* a new one and returns value to associate to the key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V: W, W, D: MutableMap<K, W>> Map<out K, V>.copyToBy(destination: D, resolve: (currentValue: W, newValue: V) -> W): D =
|
||||
copyToBy(destination) { _, currentValue, newValue -> resolve(currentValue, newValue) }
|
||||
|
||||
/**
|
||||
* Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting
|
||||
* entries to the [destination] map overriding present ones if needed. Is equivalent to
|
||||
* ```kotlin
|
||||
* this.mapValues(transform).copyTo(destination)
|
||||
* ```
|
||||
*
|
||||
* @receiver map to be transformed and copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is
|
||||
* the same as initial entry.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D: MutableMap<K, W>> Map<out K, V>.copyMapTo(destination: D, transform: (Map.Entry<K, V>) -> W): D {
|
||||
for (entry in this) {
|
||||
destination[entry.key] = transform(entry)
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting
|
||||
* entries to the [destination] map overriding present ones if needed. Is equivalent to
|
||||
* ```kotlin
|
||||
* this.mapValues(transform).copyTo(destination)
|
||||
* ```
|
||||
*
|
||||
* @receiver map to be transformed and copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is
|
||||
* the same as initial entry.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D: MutableMap<K, W>> Map<out K, V>.copyMapTo(destination: D, transform: (key: K, value: V) -> W): D =
|
||||
copyMapTo(destination) { (key, value) -> transform(key, value) }
|
||||
|
||||
/**
|
||||
* Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting
|
||||
* entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to
|
||||
* ```kotlin
|
||||
* this.mapValues(transform).copyToBy(destination, resolve)
|
||||
* ```
|
||||
*
|
||||
* @receiver map to be transformed and copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is
|
||||
* the same as initial entry.
|
||||
* @param resolve lambda function that resolves overriding. It takes a key, current value corresponding to the key, and
|
||||
* a new one and returns value to associate to the key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D: MutableMap<K, W>> Map<out K, V>.copyMapToBy(destination: D, transform: (Map.Entry<K, V>) -> W, resolve: (key: K, currentValue: W, newValue: V) -> W): D {
|
||||
for (entry in this) {
|
||||
val (key, value) = entry
|
||||
destination.putOrChange(key, transform(entry)) { it -> resolve(key, it, value) }
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting
|
||||
* entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to
|
||||
* ```kotlin
|
||||
* this.mapValues(transform).copyToBy(destination, resolve)
|
||||
* ```
|
||||
*
|
||||
* @receiver map to be transformed and copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is
|
||||
* the same as initial entry.
|
||||
* @param resolve lambda function that resolves overriding. It takes a key, current value corresponding to the key, and
|
||||
* a new one and returns value to associate to the key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D: MutableMap<K, W>> Map<out K, V>.copyMapToBy(destination: D, transform: (key: K, value: V) -> W, resolve: (key: K, currentValue: W, newValue: V) -> W): D =
|
||||
copyMapToBy(destination, { (key, value) -> transform(key, value) }, resolve)
|
||||
|
||||
/**
|
||||
* Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting
|
||||
* entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to
|
||||
* ```kotlin
|
||||
* this.mapValues(transform).copyToBy(destination, resolve)
|
||||
* ```
|
||||
*
|
||||
* @receiver map to be transformed and copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is
|
||||
* the same as initial entry.
|
||||
* @param resolve lambda function that resolves overriding. It takes current value corresponding to some key, and
|
||||
* a new one and returns value to associate to the key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D: MutableMap<K, W>> Map<out K, V>.copyMapToBy(destination: D, transform: (Map.Entry<K, V>) -> W, resolve: (currentValue: W, newValue: V) -> W): D =
|
||||
copyMapToBy(destination, transform, { _, currentValue, newValue -> resolve(currentValue, newValue) })
|
||||
|
||||
/**
|
||||
* Transforms values of entries of [this map][this] with [the given transformation][transform] and copies resulting
|
||||
* entries to the [destination] map merging present entries with new ones using [resolve] lambda. Is equivalent to
|
||||
* ```kotlin
|
||||
* this.mapValues(transform).copyToBy(destination, resolve)
|
||||
* ```
|
||||
*
|
||||
* @receiver map to be transformed and copied.
|
||||
* @param destination map to receive copies.
|
||||
* @param transform generates value of transformed entry using initial entry as an argument. Key of transformed entry is
|
||||
* the same as initial entry.
|
||||
* @param resolve lambda function that resolves overriding. It takes current value corresponding to some key, and
|
||||
* a new one and returns value to associate to the key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D: MutableMap<K, W>> Map<out K, V>.copyMapToBy(destination: D, transform: (key: K, value: V) -> W, resolve: (currentValue: W, newValue: V) -> W): D =
|
||||
copyMapToBy(destination, { (key, value) -> transform(key, value) }, { _, currentValue, newValue -> resolve(currentValue, newValue) })
|
||||
|
||||
/**
|
||||
* Merges [the first map][map1] and [the second map][map2] prioritising the second one, puts result to the [destination]
|
||||
* and returns the [destination].
|
||||
*
|
||||
* Precisely, corresponding keys and values of the received maps are put into the destination overriding existing values
|
||||
* in the [destination] if needed. For every key appearing in both maps corresponding value from the second map is
|
||||
* chosen.
|
||||
*
|
||||
* @param map1 the first (less prioritised) map to merge.
|
||||
* @param map2 the second (more prioritised) map to merge.
|
||||
* @param destination the map where result of the merge is put.
|
||||
* @return the destination.
|
||||
*/
|
||||
internal fun <K, V, D: MutableMap<in K, in V>> mergeTo(map1: Map<out K, V>, map2: Map<out K, V>, destination: D): D {
|
||||
for ((key, value) in map1) {
|
||||
destination.put(key, value)
|
||||
}
|
||||
for ((key, value) in map2) {
|
||||
destination.put(key, value)
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges [the first map][map1] and [the second map][map2] resolving conflicts with [resolve] lambda, puts result to the
|
||||
* [destination] and returns the [destination].
|
||||
*
|
||||
* Precisely, corresponding keys and values of the received maps are put into the destination overriding existing values
|
||||
* in the [destination] if needed. For every key appearing in both maps corresponding value is a result of the [resolve]
|
||||
* lambda calculated on the key and its corresponding values from the merged maps.
|
||||
*
|
||||
* @param map1 the first (less prioritised) map to merge.
|
||||
* @param map2 the second (more prioritised) map to merge.
|
||||
* @param resolve lambda function that resolves merge conflicts.
|
||||
* @param destination the map where result of the merge is put.
|
||||
* @return the destination.
|
||||
*/
|
||||
internal inline fun <K, V1: W, V2: W, W, D: MutableMap<K, W>> mergeToBy(map1: Map<out K, V1>, map2: Map<out K, V2>, destination: D, resolve: (key: K, value1: V1, value2: V2) -> W): D {
|
||||
for (key in map2.keys) {
|
||||
destination.remove(key)
|
||||
}
|
||||
for ((key, value) in map1) {
|
||||
destination.put(key, value)
|
||||
}
|
||||
for ((key, value) in map2) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
destination.putOrChange(key, value) { it -> resolve(key, it as V1, value) }
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges [the first map][map1] and [the second map][map2] resolving conflicts with [resolve] lambda, puts result to the
|
||||
* [destination] and returns the [destination].
|
||||
*
|
||||
* Precisely, corresponding keys and values of the received maps are put into the destination overriding existing values
|
||||
* in the [destination] if needed. For every key appearing in both maps corresponding value is a result of the [resolve]
|
||||
* lambda calculated on the key's corresponding values from the merged maps.
|
||||
*
|
||||
* @param map1 the first (less prioritised) map to merge.
|
||||
* @param map2 the second (more prioritised) map to merge.
|
||||
* @param resolve lambda function that resolves merge conflicts.
|
||||
* @param destination the map where result of the merge is put.
|
||||
* @return the destination.
|
||||
*/
|
||||
internal inline fun <K, V1: W, V2: W, W, D: MutableMap<K, W>> mergeToBy(map1: Map<K, V1>, map2: Map<K, V2>, destination: D, resolve: (value1: V1, value2: V2) -> W): D =
|
||||
mergeToBy(map1, map2, destination) { _, value1, value2 -> resolve(value1, value2) }
|
||||
|
||||
/**
|
||||
* Merges [the first map][map1] and [the second map][map2] prioritising the second one.
|
||||
*
|
||||
* Precisely, corresponding keys and values of the received maps are put into a new empty map which is returned after
|
||||
* afterwards. For every key appearing in both maps corresponding value from the second map is chosen.
|
||||
*
|
||||
* @param map1 the first (less prioritised) map to merge.
|
||||
* @param map2 the second (more prioritised) map to merge.
|
||||
* @return the result of the merge.
|
||||
*/
|
||||
internal fun <K, V1: W, V2: W, W> merge(map1: Map<K, V1>, map2: Map<K, V2>): Map<K, W> {
|
||||
val result = LinkedHashMap<K, W>(map1.size + map2.size)
|
||||
return mergeTo(map1, map2, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges [the first map][map1] and [the second map][map2] resolving conflicts with [resolve] lambda.
|
||||
*
|
||||
* Precisely, corresponding keys and values of the received maps are put into a new empty map which is returned after
|
||||
* afterwards. For every key appearing in both maps corresponding value is a result of the [resolve] lambda calculated
|
||||
* on the key and its corresponding values from the merged maps.
|
||||
*
|
||||
* @param map1 the first (less prioritised) map to merge.
|
||||
* @param map2 the second (more prioritised) map to merge.
|
||||
* @param resolve lambda function that resolves merge conflicts.
|
||||
* @return the result of the merge.
|
||||
*/
|
||||
internal inline fun <K, V1: W, V2: W, W> mergeBy(map1: Map<K, V1>, map2: Map<K, V2>, resolve: (key: K, value1: V1, value2: V2) -> W): Map<K, W> {
|
||||
val result = LinkedHashMap<K, W>(map1.size + map2.size)
|
||||
return mergeToBy(map1, map2, result, resolve)
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges [the first map][map1] and [the second map][map2] resolving conflicts with [resolve] lambda.
|
||||
*
|
||||
* Precisely, corresponding keys and values of the received maps are put into a new empty map which is returned after
|
||||
* afterwards. For every key appearing in both maps corresponding value is a result of the [resolve] lambda calculated
|
||||
* on the key's corresponding values from the merged maps.
|
||||
*
|
||||
* @param map1 the first (less prioritised) map to merge.
|
||||
* @param map2 the second (more prioritised) map to merge.
|
||||
* @param resolve lambda function that resolves merge conflicts.
|
||||
* @return the result of the merge.
|
||||
*/
|
||||
internal inline fun <K, V1: W, V2: W, W> mergeBy(map1: Map<K, V1>, map2: Map<K, V2>, resolve: (value1: V1, value2: V2) -> W): Map<K, W> =
|
||||
mergeBy(map1, map2) { _, value1, value2 -> resolve(value1, value2) }
|
||||
|
||||
/**
|
||||
* Populates the [destination] map with key-value pairs provided by [transform] function applied to each element of the
|
||||
* given collection resolving conflicts with [resolve] function and returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each element to key-value.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <T, K, V, D : MutableMap<K, V>> Iterable<T>.associateTo(destination: D, transform: (T) -> Pair<K, V>, resolve: (key: K, currentValue: V, newValue: V) -> V): D {
|
||||
for (element in this) {
|
||||
val (key, value) = transform(element)
|
||||
destination.putOrChange(key, value, resolve)
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the [destination] map with key-value pairs, where key is provided by [keySelector] function and value is
|
||||
* provided by [valueTransform] applied to each element of the given collection, resolving conflicts with [resolve]
|
||||
* function and returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param valueTransform lambda functions that generates value for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <T, K, V, D : MutableMap<K, V>> Iterable<T>.associateByTo(destination: D, keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (key: K, currentValue: V, newValue: V) -> V): D {
|
||||
for (element in this) {
|
||||
val key = keySelector(element)
|
||||
val value = valueTransform(element)
|
||||
destination.putOrChange(key, value, resolve)
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the [destination] map with key-value pairs, where key is provided by [keySelector] function applied to each
|
||||
* element of the given collection and value is the element itself, resolving conflicts with [resolve] function and
|
||||
* returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <T, K, D : MutableMap<K, T>> Iterable<T>.associateByTo(destination: D, keySelector: (T) -> K, resolve: (key: K, currentValue: T, newValue: T) -> T): D {
|
||||
for (element in this) {
|
||||
val key = keySelector(element)
|
||||
destination.putOrChange(key, element, resolve)
|
||||
}
|
||||
return destination
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the [destination] map with key-value pairs provided by [transform] function applied to each element of the
|
||||
* given collection resolving conflicts with [resolve] function and returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each element to key-value pair.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <T, K, V, D : MutableMap<K, V>> Iterable<T>.associateTo(destination: D, transform: (T) -> Pair<K, V>, resolve: (currentValue: V, newValue: V) -> V): D =
|
||||
associateTo(destination, transform) { _, currentValue, newValue -> resolve(currentValue, newValue) }
|
||||
|
||||
/**
|
||||
* Populates the [destination] map with key-value pairs, where key is provided by [keySelector] function and value is
|
||||
* provided by [valueTransform] applied to each element of the given collection, resolving conflicts with [resolve]
|
||||
* function and returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param valueTransform lambda functions that generates value for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <T, K, V, D : MutableMap<K, V>> Iterable<T>.associateByTo(destination: D, keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (currentValue: V, newValue: V) -> V): D =
|
||||
associateByTo(destination, keySelector, valueTransform) { _, currentValue, newValue -> resolve(currentValue, newValue) }
|
||||
|
||||
/**
|
||||
* Populates the [destination] map with key-value pairs, where key is provided by [keySelector] function applied to each
|
||||
* element of the given collection and value is the element itself, resolving conflicts with [resolve] function and
|
||||
* returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <T, K, D : MutableMap<K, T>> Iterable<T>.associateByTo(destination: D, keySelector: (T) -> K, resolve: (currentValue: T, newValue: T) -> T): D =
|
||||
associateByTo(destination, keySelector) { _, currentValue, newValue -> resolve(currentValue, newValue) }
|
||||
|
||||
/**
|
||||
* Returns a map containing key-value pairs provided by [transform] function applied to elements of the given collection.
|
||||
*
|
||||
* All pairs are added in order of iteration. If some key is already added to the map, adding new key-value pair with the
|
||||
* key is resolved with [resolve] function which takes the key, current value corresponding to the key, and new value
|
||||
* from the pair.
|
||||
*
|
||||
* @param transform function which transforms each element to key-value pair.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <T, K, V> Iterable<T>.associate(transform: (T) -> Pair<K, V>, resolve: (key: K, currentValue: V, newValue: V) -> V): Map<K, V> =
|
||||
associateTo(LinkedHashMap(), transform, resolve)
|
||||
|
||||
/**
|
||||
* Returns a map containing the values provided by [valueTransform] and indexed by [keySelector] functions applied to
|
||||
* elements of the given collection.
|
||||
*
|
||||
* All pairs are added in order of iteration. If some key is already added to the map, adding new key-value pair with
|
||||
* the key is resolved with [resolve] function which takes the key, current value corresponding to the key, and new
|
||||
* value from the pair.
|
||||
*
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param valueTransform lambda functions that generates value for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <T, K, V> Iterable<T>.associateBy(keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (key: K, currentValue: V, newValue: V) -> V): Map<K, V> =
|
||||
associateByTo(LinkedHashMap(), keySelector, valueTransform, resolve)
|
||||
|
||||
/**
|
||||
* Returns a map containing the elements from the given collection indexed by the key returned from [keySelector]
|
||||
* function applied to each element.
|
||||
*
|
||||
* All pairs are added in order of iteration. If some key is already added to the map, adding new key-value pair with
|
||||
* the key is resolved with [resolve] function which takes the key, current value corresponding to the key, and new
|
||||
* value from the pair.
|
||||
*
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <T, K> Iterable<T>.associateBy(keySelector: (T) -> K, resolve: (key: K, currentValue: T, newValue: T) -> T): Map<K, T> =
|
||||
associateByTo(LinkedHashMap(), keySelector, resolve)
|
||||
|
||||
/**
|
||||
* Returns a map containing key-value pairs provided by [transform] function applied to elements of the given collection.
|
||||
*
|
||||
* All pairs are added in order of iteration. If some key is already added to the map, adding new key-value pair with
|
||||
* the key is resolved with [resolve] function which takes current value corresponding to the key and new value from the
|
||||
* pair.
|
||||
*
|
||||
* @param transform function which transforms each element to key-value pair.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <T, K, V> Iterable<T>.associate(transform: (T) -> Pair<K, V>, resolve: (currentValue: V, newValue: V) -> V): Map<K, V> =
|
||||
associateTo(LinkedHashMap(), transform, resolve)
|
||||
|
||||
/**
|
||||
* Returns a map containing the values provided by [valueTransform] and indexed by [keySelector] functions applied to
|
||||
* elements of the given collection.
|
||||
*
|
||||
* All pairs are added in order of iteration. If some key is already added to the map, adding new key-value pair with
|
||||
* the key is resolved with [resolve] function which takes current value corresponding to the key and new value from the
|
||||
* pair.
|
||||
*
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param valueTransform lambda functions that generates value for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <T, K, V> Iterable<T>.associateBy(keySelector: (T) -> K, valueTransform: (T) -> V, resolve: (currentValue: V, newValue: V) -> V): Map<K, V> =
|
||||
associateByTo(LinkedHashMap(), keySelector, valueTransform, resolve)
|
||||
|
||||
/**
|
||||
* Returns a map containing the elements from the given collection indexed by the key returned from [keySelector]
|
||||
* function applied to each element.
|
||||
*
|
||||
* All pairs are added in order of iteration. If some key is already added to the map, adding new key-value pair with
|
||||
* the key is resolved with [resolve] function which takes current value corresponding to the key and new value from the
|
||||
* pair.
|
||||
*
|
||||
* @param keySelector lambda functions that generates keys for the key-value pairs.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <T, K> Iterable<T>.associateBy(keySelector: (T) -> K, resolve: (currentValue: T, newValue: T) -> T): Map<K, T> =
|
||||
associateByTo(LinkedHashMap(), keySelector, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys of this map and the values obtained
|
||||
* by applying the [transform] function to each entry in this map resolving conflicts with [resolve] function and
|
||||
* returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new value.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D : MutableMap<K, W>> Map<out K, V>.mapValuesTo(destination: D, transform: (Map.Entry<K, V>) -> W, resolve: (key: K, currentValue: W, newValue: W) -> W): D =
|
||||
entries.associateByTo(destination, { it.key }, transform, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys of this map and the values obtained
|
||||
* by applying the [transform] function to each entry in this map resolving conflicts with [resolve] function and
|
||||
* returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new value.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D : MutableMap<K, W>> Map<out K, V>.mapValuesTo(destination: D, transform: (key: K, value: V) -> W, resolve: (key: K, currentValue: W, newValue: W) -> W): D =
|
||||
entries.associateByTo(destination, { it.key }, { (key, value) -> transform(key, value) }, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys of this map and the values obtained
|
||||
* by applying the [transform] function to each entry in this map resolving conflicts with [resolve] function and
|
||||
* returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new value.
|
||||
* @param resolve lambda function that resolves merge conflicts which current and new values corresponding to some key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D : MutableMap<K, W>> Map<out K, V>.mapValuesTo(destination: D, transform: (Map.Entry<K, V>) -> W, resolve: (currentValue: W, newValue: W) -> W): D =
|
||||
entries.associateByTo(destination, { it.key }, transform, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys of this map and the values obtained
|
||||
* by applying the [transform] function to each entry in this map resolving conflicts with [resolve] function and
|
||||
* returns the [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new value.
|
||||
* @param resolve lambda function that resolves merge conflicts which current and new values corresponding to some key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, W, D : MutableMap<K, W>> Map<out K, V>.mapValuesTo(destination: D, transform: (key: K, value: V) -> W, resolve: (currentValue: W, newValue: W) -> W): D =
|
||||
entries.associateByTo(destination, { it.key }, { (key, value) -> transform(key, value) }, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys obtained by applying the [transform] function to
|
||||
* each entry in this map and the values of this map, resolving conflicts with [resolve] function and returns the
|
||||
* [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, L, D : MutableMap<L, V>> Map<out K, V>.mapKeysTo(destination: D, transform: (Map.Entry<K, V>) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): D =
|
||||
entries.associateByTo(destination, transform, { it.value }, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys obtained by applying the [transform] function to
|
||||
* each entry in this map and the values of this map, resolving conflicts with [resolve] function and returns the
|
||||
* [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, L, D : MutableMap<L, V>> Map<out K, V>.mapKeysTo(destination: D, transform: (key: K, value: V) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): D =
|
||||
entries.associateByTo(destination, { (key, value) -> transform(key, value) }, { it.value }, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys obtained by applying the [transform] function to
|
||||
* each entry in this map and the values of this map, resolving conflicts with [resolve] function and returns the
|
||||
* [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which current and new values corresponding to some key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, L, D : MutableMap<L, V>> Map<out K, V>.mapKeysTo(destination: D, transform: (Map.Entry<K, V>) -> L, resolve: (currentValue: V, newValue: V) -> V): D =
|
||||
entries.associateByTo(destination, transform, { it.value }, resolve)
|
||||
|
||||
/**
|
||||
* Populates the given [destination] map with entries having the keys obtained by applying the [transform] function to
|
||||
* each entry in this map and the values of this map, resolving conflicts with [resolve] function and returns the
|
||||
* [destination].
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param destination the destination of the generated key-value pairs.
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which current and new values corresponding to some key.
|
||||
* @return the [destination].
|
||||
*/
|
||||
internal inline fun <K, V, L, D : MutableMap<L, V>> Map<out K, V>.mapKeysTo(destination: D, transform: (key: K, value: V) -> L, resolve: (currentValue: V, newValue: V) -> V): D =
|
||||
entries.associateByTo(destination, { (key, value) -> transform(key, value) }, { it.value }, resolve)
|
||||
|
||||
/**
|
||||
* Returns a new map with entries having the keys obtained by applying the [transform] function to each entry in this
|
||||
* map and the values of this map and resolving conflicts with [resolve] function.
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <K, V, L> Map<out K, V>.mapKeys(transform: (Map.Entry<K, V>) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): Map<L, V> =
|
||||
mapKeysTo(LinkedHashMap(size), transform, resolve)
|
||||
|
||||
/**
|
||||
* Returns a new map with entries having the keys obtained by applying the [transform] function to each entry in this
|
||||
* map and the values of this map and resolving conflicts with [resolve] function.
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which receives some key, its current, and new
|
||||
* corresponding values.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <K, V, L> Map<out K, V>.mapKeys(transform: (key: K, value: V) -> L, resolve: (key: L, currentValue: V, newValue: V) -> V): Map<L, V> =
|
||||
mapKeysTo(LinkedHashMap(size), transform, resolve)
|
||||
|
||||
/**
|
||||
* Returns a new map with entries having the keys obtained by applying the [transform] function to each entry in this
|
||||
* map and the values of this map and resolving conflicts with [resolve] function.
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which current and new values corresponding to some key.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <K, V, L> Map<out K, V>.mapKeys(transform: (Map.Entry<K, V>) -> L, resolve: (currentValue: V, newValue: V) -> V): Map<L, V> =
|
||||
mapKeysTo(LinkedHashMap(size), transform, resolve)
|
||||
|
||||
/**
|
||||
* Returns a new map with entries having the keys obtained by applying the [transform] function to each entry in this
|
||||
* map and the values of this map and resolving conflicts with [resolve] function.
|
||||
*
|
||||
* All pairs are added and resolved in order of iteration.
|
||||
*
|
||||
* @param transform function which transforms each key-value pair to new key.
|
||||
* @param resolve lambda function that resolves merge conflicts which current and new values corresponding to some key.
|
||||
* @return the result map.
|
||||
*/
|
||||
internal inline fun <K, V, L> Map<out K, V>.mapKeys(transform: (key: K, value: V) -> L, resolve: (currentValue: V, newValue: V) -> V): Map<L, V> =
|
||||
mapKeysTo(LinkedHashMap(size), transform, resolve)
|
@ -0,0 +1,779 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName", "NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.operations.invoke
|
||||
|
||||
|
||||
/**
|
||||
* Returns the same degrees' description of the monomial, but without zero degrees.
|
||||
*/
|
||||
internal fun Map<Symbol, UInt>.cleanUp() = filterValues { it > 0U }
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided coefficients map [coefs]. The map is used as is.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal inline fun <C> LabeledPolynomialAsIs(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(coefs)
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The collections will be transformed to map with [toMap] and then will be used as is.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal inline fun <C> LabeledPolynomialAsIs(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided array of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The array will be transformed to map with [toMap] and then will be used as is.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal inline fun <C> LabeledPolynomialAsIs(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided coefficients map [coefs]. The map is used as is.
|
||||
*
|
||||
* **Be sure you read description of [LabeledPolynomial.coefficients]. Otherwise, you may make a mistake that will
|
||||
* cause wrong computation result or even runtime error.**
|
||||
*/
|
||||
@DelicatePolynomialAPI
|
||||
public inline fun <C> LabeledPolynomialWithoutCheck(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(coefs)
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The collections will be transformed to map with [toMap] and then will be used as is.
|
||||
*
|
||||
* **Be sure you read description of [LabeledPolynomial.coefficients]. Otherwise, you may make a mistake that will
|
||||
* cause wrong computation result or even runtime error.**
|
||||
*/
|
||||
@DelicatePolynomialAPI
|
||||
public inline fun <C> LabeledPolynomialWithoutCheck(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided array of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The array will be transformed to map with [toMap] and then will be used as is.
|
||||
*
|
||||
* **Be sure you read description of [LabeledPolynomial.coefficients]. Otherwise, you may make a mistake that will
|
||||
* cause wrong computation result or even runtime error.**
|
||||
*/
|
||||
@DelicatePolynomialAPI
|
||||
public inline fun <C> LabeledPolynomialWithoutCheck(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public fun <C> LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>, add: (C, C) -> C) : LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
coefs.mapKeys({ key, _ -> key.cleanUp() }, add)
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public fun <C> LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>, add: (C, C) -> C) : LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
pairs.associateBy({ it.first.cleanUp() }, { it.second }, add)
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public fun <C> LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>, add: (C, C) -> C) : LabeledPolynomial<C> =
|
||||
LabeledPolynomialAsIs(
|
||||
pairs.asIterable().associateBy({ it.first.cleanUp() }, { it.second }, add)
|
||||
)
|
||||
|
||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> A.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs, ::add)
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(coefs: Map<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(coefs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> A.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs, ::add)
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs) { left: C, right: C -> left + right }
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(pairs: Collection<Pair<Map<Symbol, UInt>, C>>) : LabeledPolynomial<C> = LabeledPolynomial(pairs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> A.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs) { left: C, right: C -> left + right }
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs) { left: C, right: C -> left + right }
|
||||
/**
|
||||
* Constructs [LabeledPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see LabeledPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomial(vararg pairs: Pair<Map<Symbol, UInt>, C>) : LabeledPolynomial<C> = LabeledPolynomial(*pairs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Converts [this] constant to [LabeledPolynomial].
|
||||
*/
|
||||
public inline fun <C> C.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this))
|
||||
|
||||
///**
|
||||
//// * Converts [this] variable to [LabeledPolynomial].
|
||||
//// */
|
||||
//context(A)
|
||||
//public inline fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to one))
|
||||
///**
|
||||
// * Converts [this] variable to [LabeledPolynomial].
|
||||
// */
|
||||
//context(LabeledPolynomialSpace<C, A>)
|
||||
//public inline fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to constantOne))
|
||||
///**
|
||||
// * Converts [this] variable to [LabeledPolynomial].
|
||||
// */
|
||||
//context(LabeledRationalFunctionSpace<C, A>)
|
||||
//public inline fun <C, A: Ring<C>> Symbol.asLabeledPolynomial() : LabeledPolynomial<C> = LabeledPolynomial<C>(mapOf(mapOf(this to 1u) to constantOne))
|
||||
|
||||
/**
|
||||
* Marks DSL that allows to more simply create [LabeledPolynomial]s with good performance.
|
||||
*
|
||||
* For example, polynomial \(5 a^2 c^3 - 6 b\) can be described as
|
||||
* ```
|
||||
* Int.algebra {
|
||||
* val labeledPolynomial : LabeledPolynomial<Int> = LabeledPolynomialDSL1 {
|
||||
* 5 { a inPowerOf 2u; c inPowerOf 3u } // 5 a^2 c^3 +
|
||||
* (-6) { b inPowerOf 1u } // (-6) b^1
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @usesMathJax
|
||||
*/
|
||||
@DslMarker
|
||||
@UnstableKMathAPI
|
||||
internal annotation class LabeledPolynomialConstructorDSL1
|
||||
|
||||
/**
|
||||
* Builder of [LabeledPolynomial] signature. It should be used as an implicit context for lambdas that describe term signature.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
@LabeledPolynomialConstructorDSL1
|
||||
public class DSL1LabeledPolynomialTermSignatureBuilder {
|
||||
/**
|
||||
* Signature storage. Any declaration of any variable's power updates the storage by increasing corresponding value.
|
||||
* Afterward the storage will be used as a resulting signature.
|
||||
*/
|
||||
private val signature: MutableMap<Symbol, UInt> = LinkedHashMap()
|
||||
|
||||
/**
|
||||
* Builds the resulting signature.
|
||||
*
|
||||
* In fact, it just returns [signature] as regular signature of type `List<UInt>`.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun build(): Map<Symbol, UInt> = signature
|
||||
|
||||
/**
|
||||
* Declares power of [this] variable of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public infix fun Symbol.inPowerOf(deg: UInt) {
|
||||
if (deg == 0u) return
|
||||
signature.putOrChange(this, deg) { it -> it + deg }
|
||||
}
|
||||
/**
|
||||
* Declares power of [this] variable of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public inline infix fun Symbol.pow(deg: UInt): Unit = this inPowerOf deg
|
||||
/**
|
||||
* Declares power of [this] variable of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public inline infix fun Symbol.`in`(deg: UInt): Unit = this inPowerOf deg
|
||||
/**
|
||||
* Declares power of [this] variable of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public inline infix fun Symbol.of(deg: UInt): Unit = this inPowerOf deg
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder of [LabeledPolynomial]. It should be used as an implicit context for lambdas that describe [LabeledPolynomial].
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
@LabeledPolynomialConstructorDSL1
|
||||
public class DSL1LabeledPolynomialBuilder<C>(
|
||||
/**
|
||||
* Summation operation that will be used to sum coefficients of monomials of same signatures.
|
||||
*/
|
||||
private val add: (C, C) -> C,
|
||||
/**
|
||||
* Initial capacity of coefficients map.
|
||||
*/
|
||||
initialCapacity: Int? = null
|
||||
) {
|
||||
/**
|
||||
* Coefficients storage. Any declaration of any monomial updates the storage.
|
||||
* Afterward the storage will be used as a resulting coefficients map.
|
||||
*/
|
||||
private val coefficients: MutableMap<Map<Symbol, UInt>, C> = if (initialCapacity != null) LinkedHashMap(initialCapacity) else LinkedHashMap()
|
||||
|
||||
/**
|
||||
* Builds the resulting coefficients map.
|
||||
*
|
||||
* In fact, it just returns [coefficients] as regular coefficients map of type `Map<Map<Symbol, UInt>, C>`.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun build(): LabeledPolynomial<C> = LabeledPolynomial<C>(coefficients)
|
||||
|
||||
/**
|
||||
* Declares monomial with [this] coefficient and provided [signature].
|
||||
*
|
||||
* Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such
|
||||
* coefficients is zero at any moment the monomial won't be removed but will be left as it is.
|
||||
*/
|
||||
public infix fun C.with(signature: Map<Symbol, UInt>) {
|
||||
coefficients.putOrChange(signature, this@with, add)
|
||||
}
|
||||
/**
|
||||
* Declares monomial with [this] coefficient and signature constructed by [block].
|
||||
*
|
||||
* Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such
|
||||
* coefficients is zero at any moment the monomial won't be removed but will be left as it is.
|
||||
*/
|
||||
public inline infix fun C.with(noinline block: DSL1LabeledPolynomialTermSignatureBuilder.() -> Unit): Unit = this.invoke(block)
|
||||
/**
|
||||
* Declares monomial with [this] coefficient and signature constructed by [block].
|
||||
*
|
||||
* Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such
|
||||
* coefficients is zero at any moment the monomial won't be removed but will be left as it is.
|
||||
*/
|
||||
public inline operator fun C.invoke(block: DSL1LabeledPolynomialTermSignatureBuilder.() -> Unit): Unit =
|
||||
this with DSL1LabeledPolynomialTermSignatureBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
||||
|
||||
///**
|
||||
// * Creates [LabeledPolynomial] with lambda [block] in context of [this] ring of constants.
|
||||
// *
|
||||
// * For example, polynomial \(5 a^2 c^3 - 6 b\) can be described as
|
||||
// * ```
|
||||
// * Int.algebra {
|
||||
// * val labeledPolynomial : LabeledPolynomial<Int> = LabeledPolynomialDSL1 {
|
||||
// * 5 { a inPowerOf 2u; c inPowerOf 3u } // 5 a^2 c^3 +
|
||||
// * (-6) { b inPowerOf 1u } // (-6) b^1
|
||||
// * }
|
||||
// * }
|
||||
// * ```
|
||||
// * @usesMathJax
|
||||
// */
|
||||
// FIXME: For now this fabric does not let next two fabrics work. (See KT-52803.) Possible feature solutions:
|
||||
// 1. `LowPriorityInOverloadResolution` becomes public. Then it should be applied to this function.
|
||||
// 2. Union types are implemented. Then all three functions should be rewritten
|
||||
// as one with single union type as a (context) receiver.
|
||||
//@UnstableKMathAPI
|
||||
//public inline fun <C, A: Ring<C>> A.LabeledPolynomialDSL1(initialCapacity: Int? = null, block: LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = LabeledPolynomialBuilder(::add, initialCapacity).apply(block).build()
|
||||
/**
|
||||
* Creates [LabeledPolynomial] with lambda [block] in context of [this] ring of [LabeledPolynomial]s.
|
||||
*
|
||||
* For example, polynomial \(5 a^2 c^3 - 6 b\) can be described as
|
||||
* ```
|
||||
* Int.algebra {
|
||||
* val labeledPolynomial : LabeledPolynomial<Int> = LabeledPolynomialDSL1 {
|
||||
* 5 { a inPowerOf 2u; c inPowerOf 3u } // 5 a^2 c^3 +
|
||||
* (-6) { b inPowerOf 1u } // (-6) b^1
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @usesMathJax
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public inline fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomialDSL1(initialCapacity: Int? = null, block: DSL1LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = DSL1LabeledPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build()
|
||||
/**
|
||||
* Creates [LabeledPolynomial] with lambda [block] in context of [this] field of [LabeledRationalFunction]s.
|
||||
*
|
||||
* For example, polynomial \(5 a^2 c^3 - 6 b\) can be described as
|
||||
* ```
|
||||
* Int.algebra {
|
||||
* val labeledPolynomial : LabeledPolynomial<Int> = LabeledPolynomialDSL1 {
|
||||
* 5 { a inPowerOf 2u; c inPowerOf 3u } // 5 a^2 c^3 +
|
||||
* (-6) { b inPowerOf 1u } // (-6) b^1
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @usesMathJax
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public inline fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomialDSL1(initialCapacity: Int? = null, block: DSL1LabeledPolynomialBuilder<C>.() -> Unit) : LabeledPolynomial<C> = DSL1LabeledPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build()
|
||||
|
||||
/**
|
||||
* Marks DSL that allows to more simply create [LabeledPolynomial]s with good performance.
|
||||
*
|
||||
* For example, polynomial \(5 a^2 c^3 - 6 b\) can be described as
|
||||
* ```
|
||||
* Int.algebra {
|
||||
* val numberedPolynomial : NumberedPolynomial<Int> = NumberedPolynomial {
|
||||
* 5 { a inPowerOf 2u; c inPowerOf 3u } // 5 a^2 c^3 +
|
||||
* (-6) { b inPowerOf 1u } // (-6) b^1
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @usesMathJax
|
||||
*/
|
||||
@DslMarker
|
||||
@UnstableKMathAPI
|
||||
internal annotation class LabeledPolynomialBuilderDSL2
|
||||
|
||||
/**
|
||||
* Builder of [LabeledPolynomial]. It should be used as an implicit context for lambdas that describe [LabeledPolynomial].
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
@LabeledPolynomialBuilderDSL2
|
||||
public class DSL2LabeledPolynomialBuilder<C>(
|
||||
private val ring: Ring<C>,
|
||||
/**
|
||||
* Initial capacity of coefficients map.
|
||||
*/
|
||||
initialCapacity: Int? = null
|
||||
) {
|
||||
/**
|
||||
* Coefficients storage. Any declaration of any monomial updates the storage.
|
||||
* Afterward the storage will be used as a resulting coefficients map.
|
||||
*/
|
||||
private val coefficients: MutableMap<Map<Symbol, UInt>, C> = if (initialCapacity != null) LinkedHashMap(initialCapacity) else LinkedHashMap()
|
||||
|
||||
/**
|
||||
* Builds the resulting coefficients map.
|
||||
*
|
||||
* In fact, it just returns [coefficients] as regular coefficients map of type `Map<Map<Symbol, UInt>, C>`.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun build(): LabeledPolynomial<C> = LabeledPolynomial<C>(coefficients)
|
||||
|
||||
public inner class Term internal constructor(
|
||||
internal val signature: Map<Symbol, UInt> = HashMap(),
|
||||
internal val coefficient: C
|
||||
)
|
||||
|
||||
private inline fun submit(signature: Map<Symbol, UInt>, onPut: Ring<C>.() -> C, onChange: Ring<C>.(C) -> C) {
|
||||
coefficients.putOrChange<_, C>(signature, { ring.onPut() }, { ring.onChange(it) })
|
||||
}
|
||||
|
||||
private inline fun submit(signature: Map<Symbol, UInt>, lazyCoefficient: Ring<C>.() -> C) {
|
||||
submit(signature, lazyCoefficient, { it + lazyCoefficient() })
|
||||
}
|
||||
|
||||
private fun submit(signature: Map<Symbol, UInt>, coefficient: C) {
|
||||
submit(signature) { coefficient }
|
||||
}
|
||||
|
||||
// TODO: `@submit` will be resolved differently. Change it to `@C`.
|
||||
private fun C.submit() = submit(emptyMap(), { this@submit })
|
||||
|
||||
private fun Symbol.submit() = submit(mapOf(this to 1u), { one })
|
||||
|
||||
private fun Term.submit(): Submit {
|
||||
submit(signature, coefficient)
|
||||
return Submit
|
||||
}
|
||||
|
||||
public object Submit
|
||||
|
||||
public operator fun C.unaryPlus(): Submit {
|
||||
submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.unaryMinus(): Submit {
|
||||
submit(emptyMap(), { -this@unaryMinus }, { it - this@unaryMinus })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.plus(other: C): Submit {
|
||||
submit(emptyMap(), { this@plus + other })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.minus(other: C): Submit {
|
||||
submit(emptyMap(), { this@minus - other })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.times(other: C): C = ring { this@times * other }
|
||||
|
||||
public operator fun C.plus(other: Symbol): Submit {
|
||||
submit(emptyMap(), this)
|
||||
submit(mapOf(other to 1u), ring.one)
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.minus(other: Symbol): Submit {
|
||||
submit(emptyMap(), this)
|
||||
submit(mapOf(other to 1u), { -one }, { it - one })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.times(other: Symbol): Term = Term(mapOf(other to 1u), this)
|
||||
|
||||
public operator fun C.plus(other: Term): Submit {
|
||||
submit(emptyMap(), this)
|
||||
other.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.minus(other: Term): Submit {
|
||||
submit(emptyMap(), this)
|
||||
submit(other.signature, { -other.coefficient }, { it - other.coefficient })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun C.times(other: Term): Term = Term(other.signature, ring { this@times * other.coefficient })
|
||||
|
||||
public operator fun Symbol.plus(other: C): Submit {
|
||||
this.submit()
|
||||
other.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.minus(other: C): Submit {
|
||||
this.submit()
|
||||
submit(emptyMap(), { -other }, { it - other })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.times(other: C): Term = Term(mapOf(this to 1u), other)
|
||||
|
||||
public operator fun Symbol.unaryPlus(): Submit {
|
||||
this.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.unaryMinus(): Submit {
|
||||
submit(mapOf(this to 1u), { -one }, { it - one })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.plus(other: Symbol): Submit {
|
||||
this.submit()
|
||||
other.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.minus(other: Symbol): Submit {
|
||||
this.submit()
|
||||
submit(mapOf(other to 1u), { -one }, { it - one })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.times(other: Symbol): Term =
|
||||
if (this == other) Term(mapOf(this to 2u), ring.one)
|
||||
else Term(mapOf(this to 1u, other to 1u), ring.one)
|
||||
|
||||
public operator fun Symbol.plus(other: Term): Submit {
|
||||
this.submit()
|
||||
other.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.minus(other: Term): Submit {
|
||||
this.submit()
|
||||
submit(other.signature, { -other.coefficient }, { it - other.coefficient })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Symbol.times(other: Term): Term =
|
||||
Term(
|
||||
other.signature.withPutOrChanged(this, 1u) { it -> it + 1u },
|
||||
other.coefficient
|
||||
)
|
||||
|
||||
public operator fun Term.plus(other: C): Submit {
|
||||
this.submit()
|
||||
other.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.minus(other: C): Submit {
|
||||
this.submit()
|
||||
submit(emptyMap(), { -other }, { it - other })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.times(other: C): Term =
|
||||
Term(
|
||||
signature,
|
||||
ring { coefficient * other }
|
||||
)
|
||||
|
||||
public operator fun Term.plus(other: Symbol): Submit {
|
||||
this.submit()
|
||||
other.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.minus(other: Symbol): Submit {
|
||||
this.submit()
|
||||
submit(mapOf(other to 1u), { -one }, { it - one })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.times(other: Symbol): Term =
|
||||
Term(
|
||||
signature.withPutOrChanged(other, 1u) { it -> it + 1u },
|
||||
coefficient
|
||||
)
|
||||
|
||||
public operator fun Term.unaryPlus(): Submit {
|
||||
this.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.unaryMinus(): Submit {
|
||||
submit(signature, { -coefficient }, { it - coefficient })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.plus(other: Term): Submit {
|
||||
this.submit()
|
||||
other.submit()
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.minus(other: Term): Submit {
|
||||
this.submit()
|
||||
submit(other.signature, { -other.coefficient }, { it - other.coefficient })
|
||||
return Submit
|
||||
}
|
||||
|
||||
public operator fun Term.times(other: Term): Term =
|
||||
Term(
|
||||
mergeBy(signature, other.signature) { deg1, deg2 -> deg1 + deg2 },
|
||||
ring { coefficient * other.coefficient }
|
||||
)
|
||||
}
|
||||
|
||||
//@UnstableKMathAPI
|
||||
//public fun <C> Ring<C>.LabeledPolynomialDSL2(initialCapacity: Int? = null, block: DSL2LabeledPolynomialBuilder<C>.() -> Unit): LabeledPolynomial<C> = DSL2LabeledPolynomialBuilder(this, initialCapacity).apply(block).build()
|
||||
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A: Ring<C>> LabeledPolynomialSpace<C, A>.LabeledPolynomialDSL2(initialCapacity: Int? = null, block: DSL2LabeledPolynomialBuilder<C>.() -> Unit): LabeledPolynomial<C> = DSL2LabeledPolynomialBuilder(ring, initialCapacity).apply(block).build()
|
||||
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledPolynomialDSL2(initialCapacity: Int? = null, block: DSL2LabeledPolynomialBuilder<C>.() -> Unit): LabeledPolynomial<C> = DSL2LabeledPolynomialBuilder(ring, initialCapacity).apply(block).build()
|
||||
|
||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
||||
|
||||
/**
|
||||
* Constructs [LabeledRationalFunction] with provided coefficients maps [numeratorCoefficients] and [denominatorCoefficients].
|
||||
*
|
||||
* The maps will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. the maps' keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>, denominatorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction<C>(
|
||||
LabeledPolynomial(numeratorCoefficients),
|
||||
LabeledPolynomial(denominatorCoefficients)
|
||||
)
|
||||
/**
|
||||
* Constructs [LabeledRationalFunction] with provided coefficients maps [numeratorCoefficients] and [denominatorCoefficients].
|
||||
*
|
||||
* The maps will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. the maps' keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>, denominatorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction<C>(
|
||||
LabeledPolynomial(numeratorCoefficients),
|
||||
LabeledPolynomial(denominatorCoefficients)
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [LabeledRationalFunction] with provided [numerator] and unit denominator.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction<C>(numerator, LabeledPolynomial(mapOf(emptyMap<Symbol, UInt>() to one)))
|
||||
/**
|
||||
* Constructs [LabeledRationalFunction] with provided [numerator] and unit denominator.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numerator: LabeledPolynomial<C>): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction<C>(numerator, polynomialOne)
|
||||
|
||||
/**
|
||||
* Constructs [LabeledRationalFunction] with provided coefficients map [numeratorCoefficients] for numerator and unit
|
||||
* denominator.
|
||||
*
|
||||
* [numeratorCoefficients] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [numeratorCoefficients]'s keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> LabeledRationalFunctionSpace<C, A>.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction<C>(
|
||||
LabeledPolynomial(numeratorCoefficients),
|
||||
polynomialOne
|
||||
)
|
||||
/**
|
||||
* Constructs [LabeledRationalFunction] with provided coefficients map [numeratorCoefficients] for numerator and unit
|
||||
* denominator.
|
||||
*
|
||||
* [numeratorCoefficients] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [numeratorCoefficients]'s keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> A.LabeledRationalFunction(numeratorCoefficients: Map<Map<Symbol, UInt>, C>): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction<C>(
|
||||
LabeledPolynomial(numeratorCoefficients),
|
||||
LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to one))
|
||||
)
|
||||
|
||||
///**
|
||||
// * Converts [this] constant to [LabeledRationalFunction].
|
||||
// */
|
||||
//context(A)
|
||||
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> =
|
||||
// LabeledRationalFunction(
|
||||
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this)),
|
||||
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to one))
|
||||
// )
|
||||
///**
|
||||
// * Converts [this] constant to [LabeledRationalFunction].
|
||||
// */
|
||||
//context(LabeledRationalFunctionSpace<C, A>)
|
||||
//public fun <C, A: Ring<C>> C.asLabeledRationalFunction() : LabeledRationalFunction<C> =
|
||||
// LabeledRationalFunction(
|
||||
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to this)),
|
||||
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to constantOne))
|
||||
// )
|
||||
|
||||
///**
|
||||
// * Converts [this] variable to [LabeledRationalFunction].
|
||||
// */
|
||||
//context(A)
|
||||
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> =
|
||||
// LabeledRationalFunction(
|
||||
// LabeledPolynomialAsIs(mapOf(mapOf(this to 1u) to one)),
|
||||
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to one))
|
||||
// )
|
||||
///**
|
||||
// * Converts [this] variable to [LabeledRationalFunction].
|
||||
// */
|
||||
//context(LabeledRationalFunctionSpace<C, A>)
|
||||
//public fun <C, A: Ring<C>> Symbol.asLabeledRationalFunction() : LabeledRationalFunction<C> =
|
||||
// LabeledRationalFunction(
|
||||
// LabeledPolynomialAsIs(mapOf(mapOf(this to 1u) to constantOne)),
|
||||
// LabeledPolynomialAsIs(mapOf(emptyMap<Symbol, UInt>() to constantOne))
|
||||
// )
|
@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.*
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
/**
|
||||
* Creates a [LabeledPolynomialSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A : Ring<C>> A.labeledPolynomialSpace: LabeledPolynomialSpace<C, A>
|
||||
get() = LabeledPolynomialSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [LabeledPolynomialSpace]'s scope over a received ring.
|
||||
*/
|
||||
public inline fun <C, A : Ring<C>, R> A.labeledPolynomialSpace(block: LabeledPolynomialSpace<C, A>.() -> R): R {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return LabeledPolynomialSpace(this).block()
|
||||
}
|
||||
/**
|
||||
* Creates a [LabeledRationalFunctionSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A : Ring<C>> A.labeledRationalFunctionSpace: LabeledRationalFunctionSpace<C, A>
|
||||
get() = LabeledRationalFunctionSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [LabeledRationalFunctionSpace]'s scope over a received ring.
|
||||
*/
|
||||
public inline fun <C, A : Ring<C>, R> A.labeledRationalFunctionSpace(block: LabeledRationalFunctionSpace<C, A>.() -> R): R {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return LabeledRationalFunctionSpace(this).block()
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double polynomial.
|
||||
*/
|
||||
public fun LabeledPolynomial<Double>.substitute(args: Map<Symbol, Double>): LabeledPolynomial<Double> = Double.algebra {
|
||||
if (coefficients.isEmpty()) return this@substitute
|
||||
LabeledPolynomial<Double>(
|
||||
buildMap {
|
||||
coefficients.forEach { (degs, c) ->
|
||||
val newDegs = degs.filterKeys { it !in args }
|
||||
val newC = args.entries.fold(c) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * power(substitution, deg)
|
||||
}
|
||||
putOrChange(newDegs, newC, ::add)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/
|
||||
public fun <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, args: Map<Symbol, C>): LabeledPolynomial<C> = ring {
|
||||
if (coefficients.isEmpty()) return this@substitute
|
||||
LabeledPolynomial<C>(
|
||||
buildMap {
|
||||
coefficients.forEach { (degs, c) ->
|
||||
val newDegs = degs.filterKeys { it !in args }
|
||||
val newC = args.entries.fold(c) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * power(substitution, deg)
|
||||
}
|
||||
putOrChange(newDegs, newC, ::add)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/ // TODO: To optimize boxing
|
||||
@JvmName("substitutePolynomial")
|
||||
public fun <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledPolynomial<C>>) : LabeledPolynomial<C> =
|
||||
ring.labeledPolynomialSpace {
|
||||
coefficients.entries.fold(zero) { acc, (degs, c) ->
|
||||
val newDegs = degs.filterKeys { it !in args }
|
||||
acc + args.entries.fold(LabeledPolynomial<C>(mapOf(newDegs to c))) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * power(substitution, deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/ // TODO: To optimize boxing
|
||||
@JvmName("substituteRationalFunction")
|
||||
public fun <C> LabeledPolynomial<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledRationalFunction<C>>) : LabeledRationalFunction<C> =
|
||||
ring.labeledRationalFunctionSpace {
|
||||
coefficients.entries.fold(zero) { acc, (degs, c) ->
|
||||
val newDegs = degs.filterKeys { it !in args }
|
||||
acc + args.entries.fold(LabeledRationalFunction(LabeledPolynomial<C>(mapOf(newDegs to c)))) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * power(substitution, deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double rational function.
|
||||
*/
|
||||
public fun LabeledRationalFunction<Double>.substitute(args: Map<Symbol, Double>): LabeledRationalFunction<Double> =
|
||||
LabeledRationalFunction(numerator.substitute(args), denominator.substitute(args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/
|
||||
public fun <C> LabeledRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Symbol, C>): LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/ // TODO: To optimize calculation
|
||||
@JvmName("substitutePolynomial")
|
||||
public fun <C> LabeledRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledPolynomial<C>>) : LabeledRationalFunction<C> =
|
||||
LabeledRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/ // TODO: To optimize calculation
|
||||
@JvmName("substituteRationalFunction")
|
||||
public fun <C> LabeledRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Symbol, LabeledRationalFunction<C>>) : LabeledRationalFunction<C> =
|
||||
ring.labeledRationalFunctionSpace {
|
||||
numerator.substitute(ring, args) / denominator.substitute(ring, args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variable.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Ring<C>> LabeledPolynomial<C>.derivativeWithRespectTo(
|
||||
algebra: A,
|
||||
variable: Symbol,
|
||||
): LabeledPolynomial<C> = algebra {
|
||||
LabeledPolynomial<C>(
|
||||
buildMap(coefficients.count { it.key.getOrElse(variable) { 0u } >= 1u }) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
if (variable !in degs) return@forEach
|
||||
put(
|
||||
buildMap {
|
||||
degs.forEach { (vari, deg) ->
|
||||
when {
|
||||
vari != variable -> put(vari, deg)
|
||||
deg > 1u -> put(vari, deg - 1u)
|
||||
}
|
||||
}
|
||||
},
|
||||
multiplyByDoubling(c, degs[variable]!!)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variable of specified order.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Ring<C>> LabeledPolynomial<C>.nthDerivativeWithRespectTo(
|
||||
algebra: A,
|
||||
variable: Symbol,
|
||||
order: UInt
|
||||
): LabeledPolynomial<C> = algebra {
|
||||
if (order == 0u) return this@nthDerivativeWithRespectTo
|
||||
LabeledPolynomial<C>(
|
||||
buildMap(coefficients.count { it.key.getOrElse(variable) { 0u } >= order }) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
if (degs.getOrElse(variable) { 0u } < order) return@forEach
|
||||
put(
|
||||
buildMap {
|
||||
degs.forEach { (vari, deg) ->
|
||||
when {
|
||||
vari != variable -> put(vari, deg)
|
||||
deg > order -> put(vari, deg - order)
|
||||
}
|
||||
}
|
||||
},
|
||||
degs[variable]!!.let { deg ->
|
||||
(deg downTo deg - order + 1u)
|
||||
.fold(c) { acc, ord -> multiplyByDoubling(acc, ord) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variables of specified orders.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Ring<C>> LabeledPolynomial<C>.nthDerivativeWithRespectTo(
|
||||
algebra: A,
|
||||
variablesAndOrders: Map<Symbol, UInt>,
|
||||
): LabeledPolynomial<C> = algebra {
|
||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthDerivativeWithRespectTo
|
||||
LabeledPolynomial<C>(
|
||||
buildMap(
|
||||
coefficients.count {
|
||||
variablesAndOrders.all { (variable, order) ->
|
||||
it.key.getOrElse(variable) { 0u } >= order
|
||||
}
|
||||
}
|
||||
) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
if (filteredVariablesAndOrders.any { (variable, order) -> degs.getOrElse(variable) { 0u } < order }) return@forEach
|
||||
put(
|
||||
buildMap {
|
||||
degs.forEach { (vari, deg) ->
|
||||
if (vari !in filteredVariablesAndOrders) put(vari, deg)
|
||||
else {
|
||||
val order = filteredVariablesAndOrders[vari]!!
|
||||
if (deg > order) put(vari, deg - order)
|
||||
}
|
||||
}
|
||||
},
|
||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
|
||||
degs[index]!!.let { deg ->
|
||||
(deg downTo deg - order + 1u)
|
||||
.fold(acc1) { acc2, ord -> multiplyByDoubling(acc2, ord) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic antiderivative of received polynomial with respect to provided variable.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Field<C>> LabeledPolynomial<C>.antiderivativeWithRespectTo(
|
||||
algebra: A,
|
||||
variable: Symbol,
|
||||
): LabeledPolynomial<C> = algebra {
|
||||
LabeledPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
val newDegs = degs.withPutOrChanged(variable, 1u) { it -> it + 1u }
|
||||
put(
|
||||
newDegs,
|
||||
c / multiplyByDoubling(one, newDegs[variable]!!)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic antiderivative of received polynomial with respect to provided variable of specified order.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Field<C>> LabeledPolynomial<C>.nthAntiderivativeWithRespectTo(
|
||||
algebra: A,
|
||||
variable: Symbol,
|
||||
order: UInt
|
||||
): LabeledPolynomial<C> = algebra {
|
||||
if (order == 0u) return this@nthAntiderivativeWithRespectTo
|
||||
LabeledPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
val newDegs = degs.withPutOrChanged(variable, order) { it -> it + order }
|
||||
put(
|
||||
newDegs,
|
||||
newDegs[variable]!!.let { deg ->
|
||||
(deg downTo deg - order + 1u)
|
||||
.fold(c) { acc, ord -> acc / multiplyByDoubling(one, ord) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variables of specified orders.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Field<C>> LabeledPolynomial<C>.nthAntiderivativeWithRespectTo(
|
||||
algebra: A,
|
||||
variablesAndOrders: Map<Symbol, UInt>,
|
||||
): LabeledPolynomial<C> = algebra {
|
||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthAntiderivativeWithRespectTo
|
||||
LabeledPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
val newDegs = mergeBy(degs, filteredVariablesAndOrders) { deg, order -> deg + order }
|
||||
put(
|
||||
newDegs,
|
||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
|
||||
newDegs[index]!!.let { deg ->
|
||||
(deg downTo deg - order + 1u)
|
||||
.fold(acc1) { acc2, ord -> acc2 / multiplyByDoubling(one, ord) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a [ListPolynomial] instance with provided [coefficients]. The collection of coefficients will be reversed
|
||||
* if [reverse] parameter is true.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C> ListPolynomial(coefficients: List<C>, reverse: Boolean = false): ListPolynomial<C> =
|
||||
ListPolynomial(with(coefficients) { if (reverse) reversed() else this })
|
||||
|
||||
/**
|
||||
* Constructs a [ListPolynomial] instance with provided [coefficients]. The collection of coefficients will be reversed
|
||||
* if [reverse] parameter is true.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C> ListPolynomial(vararg coefficients: C, reverse: Boolean = false): ListPolynomial<C> =
|
||||
ListPolynomial(with(coefficients) { if (reverse) reversed() else toList() })
|
||||
|
||||
/**
|
||||
* Represents [this] constant as a [ListPolynomial].
|
||||
*/
|
||||
public fun <C> C.asListPolynomial() : ListPolynomial<C> = ListPolynomial(listOf(this))
|
||||
|
||||
|
||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
||||
|
||||
/**
|
||||
* Constructs [ListRationalFunction] instance with numerator and denominator constructed with provided
|
||||
* [numeratorCoefficients] and [denominatorCoefficients]. The both collections of coefficients will be reversed if
|
||||
* [reverse] parameter is true.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C> ListRationalFunction(numeratorCoefficients: List<C>, denominatorCoefficients: List<C>, reverse: Boolean = false): ListRationalFunction<C> =
|
||||
ListRationalFunction<C>(
|
||||
ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ),
|
||||
ListPolynomial( with(denominatorCoefficients) { if (reverse) reversed() else this } )
|
||||
)
|
||||
/**
|
||||
* Constructs [ListRationalFunction] instance with provided [numerator] and unit denominator.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C, A: Ring<C>> A.ListRationalFunction(numerator: ListPolynomial<C>): ListRationalFunction<C> =
|
||||
ListRationalFunction<C>(numerator, ListPolynomial(listOf(one)))
|
||||
/**
|
||||
* Constructs [ListRationalFunction] instance with provided [numerator] and unit denominator.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C, A: Ring<C>> ListRationalFunctionSpace<C, A>.ListRationalFunction(numerator: ListPolynomial<C>): ListRationalFunction<C> =
|
||||
ListRationalFunction<C>(numerator, polynomialOne)
|
||||
/**
|
||||
* Constructs [ListRationalFunction] instance with numerator constructed with provided [numeratorCoefficients] and unit
|
||||
* denominator. The collection of numerator coefficients will be reversed if [reverse] parameter is true.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C, A: Ring<C>> A.ListRationalFunction(numeratorCoefficients: List<C>, reverse: Boolean = false): ListRationalFunction<C> =
|
||||
ListRationalFunction<C>(
|
||||
ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ),
|
||||
ListPolynomial(listOf(one))
|
||||
)
|
||||
/**
|
||||
* Constructs [ListRationalFunction] instance with numerator constructed with provided [numeratorCoefficients] and unit
|
||||
* denominator. The collection of numerator coefficients will be reversed if [reverse] parameter is true.
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public fun <C, A: Ring<C>> ListRationalFunctionSpace<C, A>.ListRationalFunction(numeratorCoefficients: List<C>, reverse: Boolean = false): ListRationalFunction<C> =
|
||||
ListRationalFunction<C>(
|
||||
ListPolynomial( with(numeratorCoefficients) { if (reverse) reversed() else this } ),
|
||||
polynomialOne
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents [this] constant as a rational function.
|
||||
*/ // FIXME: When context receivers will be ready, delete this function and uncomment the following two
|
||||
public fun <C, A: Ring<C>> C.asListRationalFunction(ring: A) : ListRationalFunction<C> = ring.ListRationalFunction(asListPolynomial())
|
||||
///**
|
||||
// * Represents [this] constant as a rational function.
|
||||
// */
|
||||
//context(A)
|
||||
//public fun <C, A: Ring<C>> C.asListRationalFunction() : ListRationalFunction<C> = ListRationalFunction(asListPolynomial())
|
||||
///**
|
||||
// * Represents [this] constant as a rational function.
|
||||
// */
|
||||
//context(ListRationalFunctionSpace<C, A>)
|
||||
//public fun <C, A: Ring<C>> C.asListRationalFunction() : ListRationalFunction<C> = ListRationalFunction(asListPolynomial())
|
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.*
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.math.max
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
/**
|
||||
* Creates a [ListPolynomialSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A : Ring<C>> A.listPolynomialSpace: ListPolynomialSpace<C, A>
|
||||
get() = ListPolynomialSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [ListPolynomialSpace]'s scope over a received ring.
|
||||
*/ // TODO: When context will be ready move [ListPolynomialSpace] and add [A] to context receivers of [block]
|
||||
public inline fun <C, A : Ring<C>, R> A.listPolynomialSpace(block: ListPolynomialSpace<C, A>.() -> R): R {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return ListPolynomialSpace(this).block()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [ScalableListPolynomialSpace] over a received scalable ring.
|
||||
*/
|
||||
public inline val <C, A> A.scalableListPolynomialSpace: ScalableListPolynomialSpace<C, A> where A : Ring<C>, A : ScaleOperations<C>
|
||||
get() = ScalableListPolynomialSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [ScalableListPolynomialSpace]'s scope over a received scalable ring.
|
||||
*/ // TODO: When context will be ready move [ListPolynomialSpace] and add [A] to context receivers of [block]
|
||||
public inline fun <C, A, R> A.scalableListPolynomialSpace(block: ScalableListPolynomialSpace<C, A>.() -> R): R where A : Ring<C>, A : ScaleOperations<C> {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return ScalableListPolynomialSpace(this).block()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [ListRationalFunctionSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A : Ring<C>> A.listRationalFunctionSpace: ListRationalFunctionSpace<C, A>
|
||||
get() = ListRationalFunctionSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [ListRationalFunctionSpace]'s scope over a received ring.
|
||||
*/ // TODO: When context will be ready move [ListRationalFunctionSpace] and add [A] to context receivers of [block]
|
||||
public inline fun <C, A : Ring<C>, R> A.listRationalFunctionSpace(block: ListRationalFunctionSpace<C, A>.() -> R): R {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return ListRationalFunctionSpace(this).block()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] Double polynomial on provided Double argument.
|
||||
*/
|
||||
public fun ListPolynomial<Double>.substitute(arg: Double): Double =
|
||||
coefficients.reduceIndexedOrNull { index, acc, c ->
|
||||
acc + c * arg.pow(index)
|
||||
} ?: .0
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial on provided argument.
|
||||
*
|
||||
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
|
||||
*/
|
||||
public fun <C> ListPolynomial<C>.substitute(ring: Ring<C>, arg: C): C = ring {
|
||||
if (coefficients.isEmpty()) return zero
|
||||
var result: C = coefficients.last()
|
||||
for (j in coefficients.size - 2 downTo 0) {
|
||||
result = (arg * result) + coefficients[j]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided polynomial [arg] into [this] polynomial.
|
||||
*
|
||||
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
|
||||
*/ // TODO: To optimize boxing
|
||||
public fun <C> ListPolynomial<C>.substitute(ring: Ring<C>, arg: ListPolynomial<C>) : ListPolynomial<C> =
|
||||
ring.listPolynomialSpace {
|
||||
if (coefficients.isEmpty()) return zero
|
||||
var result: ListPolynomial<C> = coefficients.last().asPolynomial()
|
||||
for (j in coefficients.size - 2 downTo 0) {
|
||||
result = (arg * result) + coefficients[j]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided rational function [arg] into [this] polynomial.
|
||||
*
|
||||
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
|
||||
*/ // TODO: To optimize boxing
|
||||
public fun <C> ListPolynomial<C>.substitute(ring: Ring<C>, arg: ListRationalFunction<C>) : ListRationalFunction<C> =
|
||||
ring.listRationalFunctionSpace {
|
||||
if (coefficients.isEmpty()) return zero
|
||||
var result: ListRationalFunction<C> = coefficients.last().asRationalFunction()
|
||||
for (j in coefficients.size - 2 downTo 0) {
|
||||
result = (arg * result) + coefficients[j]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] Double rational function in provided Double argument.
|
||||
*/
|
||||
public fun ListRationalFunction<Double>.substitute(arg: Double): Double =
|
||||
numerator.substitute(arg) / denominator.substitute(arg)
|
||||
|
||||
/**
|
||||
* Evaluates value of [this] polynomial for provided argument.
|
||||
*
|
||||
* It is an implementation of [Horner's method](https://en.wikipedia.org/wiki/Horner%27s_method).
|
||||
*/
|
||||
public fun <C> ListRationalFunction<C>.substitute(ring: Field<C>, arg: C): C = ring {
|
||||
numerator.substitute(ring, arg) / denominator.substitute(ring, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided polynomial [arg] into [this] rational function.
|
||||
*/ // TODO: To optimize boxing
|
||||
public fun <C> ListRationalFunction<C>.substitute(ring: Ring<C>, arg: ListPolynomial<C>) : ListRationalFunction<C> =
|
||||
ring.listRationalFunctionSpace {
|
||||
numerator.substitute(ring, arg) / denominator.substitute(ring, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided rational function [arg] into [this] rational function.
|
||||
*/ // TODO: To optimize boxing
|
||||
public fun <C> ListRationalFunction<C>.substitute(ring: Ring<C>, arg: ListRationalFunction<C>) : ListRationalFunction<C> =
|
||||
ring.listRationalFunctionSpace {
|
||||
numerator.substitute(ring, arg) / denominator.substitute(ring, arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOver(ring: A): (C) -> C = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOfConstantOver(ring: A): (C) -> C = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOfPolynomialOver(ring: A): (ListPolynomial<C>) -> ListPolynomial<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> ListPolynomial<C>.asFunctionOfRationalFunctionOver(ring: A): (ListRationalFunction<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Field<C>> ListRationalFunction<C>.asFunctionOver(ring: A): (C) -> C = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Field<C>> ListRationalFunction<C>.asFunctionOfConstantOver(ring: A): (C) -> C = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> ListRationalFunction<C>.asFunctionOfPolynomialOver(ring: A): (ListPolynomial<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> ListRationalFunction<C>.asFunctionOfRationalFunctionOver(ring: A): (ListRationalFunction<C>) -> ListRationalFunction<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> ListPolynomial<C>.derivative(
|
||||
ring: A,
|
||||
): ListPolynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = ring {
|
||||
ListPolynomial(
|
||||
buildList(max(0, coefficients.size - 1)) {
|
||||
for (deg in 1 .. coefficients.lastIndex) add(number(deg) * coefficients[deg])
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial of specified [order]. The [order] should be non-negative integer.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> ListPolynomial<C>.nthDerivative(
|
||||
ring: A,
|
||||
order: Int,
|
||||
): ListPolynomial<C> where A : Ring<C>, A : NumericAlgebra<C> = ring {
|
||||
require(order >= 0) { "Order of derivative must be non-negative" }
|
||||
ListPolynomial(
|
||||
buildList(max(0, coefficients.size - order)) {
|
||||
for (deg in order.. coefficients.lastIndex)
|
||||
add((deg - order + 1 .. deg).fold(coefficients[deg]) { acc, d -> acc * number(d) })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic antiderivative of received polynomial.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> ListPolynomial<C>.antiderivative(
|
||||
ring: A,
|
||||
): ListPolynomial<C> where A : Field<C>, A : NumericAlgebra<C> = ring {
|
||||
ListPolynomial(
|
||||
buildList(coefficients.size + 1) {
|
||||
add(zero)
|
||||
coefficients.mapIndexedTo(this) { index, t -> t / number(index + 1) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic antiderivative of received polynomial of specified [order]. The [order] should be non-negative integer.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A> ListPolynomial<C>.nthAntiderivative(
|
||||
ring: A,
|
||||
order: Int,
|
||||
): ListPolynomial<C> where A : Field<C>, A : NumericAlgebra<C> = ring {
|
||||
require(order >= 0) { "Order of antiderivative must be non-negative" }
|
||||
ListPolynomial(
|
||||
buildList(coefficients.size + order) {
|
||||
repeat(order) { add(zero) }
|
||||
coefficients.mapIndexedTo(this) { index, c -> (1..order).fold(c) { acc, i -> acc / number(index + i) } }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a definite integral of [this] polynomial in the specified [range].
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C : Comparable<C>> ListPolynomial<C>.integrate(
|
||||
ring: Field<C>,
|
||||
range: ClosedRange<C>,
|
||||
): C = ring {
|
||||
val antiderivative = antiderivative(ring)
|
||||
antiderivative.substitute(ring, range.endInclusive) - antiderivative.substitute(ring, range.start)
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
|
||||
/**
|
||||
* Marks declarations that give access to internal entities of polynomials delicate structure. Thus, it allows to
|
||||
* optimize performance a bit by skipping standard steps, but such skips may cause critical errors if something is
|
||||
* implemented badly. Make sure you fully read and understand documentation and don't break internal contracts.
|
||||
*/
|
||||
@RequiresOptIn(
|
||||
message = "This declaration gives access to delicate internal structure of polynomials. " +
|
||||
"It allows to optimize performance by skipping unnecessary arguments check. " +
|
||||
"But at the same time makes it easy to make a mistake " +
|
||||
"that will cause wrong computation result or even runtime error. " +
|
||||
"Make sure you fully read and understand documentation.",
|
||||
level = RequiresOptIn.Level.ERROR
|
||||
)
|
||||
public annotation class DelicatePolynomialAPI
|
@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName", "NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.Ring
|
||||
|
||||
|
||||
/**
|
||||
* Returns the same degrees' description of the monomial, but without extra zero degrees on the end.
|
||||
*/
|
||||
internal fun List<UInt>.cleanUp() = subList(0, indexOfLast { it != 0U } + 1)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided coefficients map [coefs]. The map is used as is.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal inline fun <C> NumberedPolynomialAsIs(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(coefs)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The collections will be transformed to map with [toMap] and then will be used as is.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal inline fun <C> NumberedPolynomialAsIs(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided array of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The array will be transformed to map with [toMap] and then will be used as is.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal inline fun <C> NumberedPolynomialAsIs(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided coefficients map [coefs]. The map is used as is.
|
||||
*
|
||||
* **Be sure you read description of [NumberedPolynomial.coefficients]. Otherwise, you may make a mistake that will
|
||||
* cause wrong computation result or even runtime error.**
|
||||
*/
|
||||
@DelicatePolynomialAPI
|
||||
public inline fun <C> NumberedPolynomialWithoutCheck(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(coefs)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The collections will be transformed to map with [toMap] and then will be used as is.
|
||||
*
|
||||
* **Be sure you read description of [NumberedPolynomial.coefficients]. Otherwise, you may make a mistake that will
|
||||
* cause wrong computation result or even runtime error.**
|
||||
*/
|
||||
@DelicatePolynomialAPI
|
||||
public inline fun <C> NumberedPolynomialWithoutCheck(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided array of [pairs] of pairs "term's signature — term's coefficient".
|
||||
* The array will be transformed to map with [toMap] and then will be used as is.
|
||||
*
|
||||
* **Be sure you read description of [NumberedPolynomial.coefficients]. Otherwise, you may make a mistake that will
|
||||
* cause wrong computation result or even runtime error.**
|
||||
*/
|
||||
@DelicatePolynomialAPI
|
||||
public inline fun <C> NumberedPolynomialWithoutCheck(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial<C>(pairs.toMap())
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public fun <C> NumberedPolynomial(coefs: Map<List<UInt>, C>, add: (C, C) -> C) : NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
coefs.mapKeys({ key, _ -> key.cleanUp() }, add)
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public fun <C> NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>, add: (C, C) -> C) : NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
pairs.associateBy({ it.first.cleanUp() }, { it.second }, add)
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public fun <C> NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>, add: (C, C) -> C) : NumberedPolynomial<C> =
|
||||
NumberedPolynomialAsIs(
|
||||
pairs.asIterable().associateBy({ it.first.cleanUp() }, { it.second }, add)
|
||||
)
|
||||
|
||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> A.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs, ::add)
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided coefficients map [coefs].
|
||||
*
|
||||
* [coefs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [coefs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(coefs: Map<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(coefs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> A.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs, ::add)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs) { left: C, right: C -> left + right }
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided collection of [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(pairs: Collection<Pair<List<UInt>, C>>) : NumberedPolynomial<C> = NumberedPolynomial(pairs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> A.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs) { left: C, right: C -> left + right }
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs) { left: C, right: C -> left + right }
|
||||
/**
|
||||
* Constructs [NumberedPolynomial] with provided array [pairs] of pairs "term's signature — term's coefficient".
|
||||
*
|
||||
* [pairs] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [pairs] keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*
|
||||
* @see NumberedPolynomialWithoutCheck
|
||||
*/
|
||||
public inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomial(vararg pairs: Pair<List<UInt>, C>) : NumberedPolynomial<C> = NumberedPolynomial(*pairs) { left: C, right: C -> left + right }
|
||||
|
||||
/**
|
||||
* Converts [this] constant to [NumberedPolynomial].
|
||||
*/
|
||||
public inline fun <C> C.asNumberedPolynomial() : NumberedPolynomial<C> = NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to this))
|
||||
|
||||
/**
|
||||
* Marks DSL that allows to more simply create [NumberedPolynomial]s with good performance.
|
||||
*
|
||||
* For example, polynomial \(5 x_0^2 x_2^3 - 6 x_1\) can be described as
|
||||
* ```
|
||||
* Int.algebra {
|
||||
* val numberedPolynomial : NumberedPolynomial<Int> = NumberedPolynomial {
|
||||
* 5 { 0 inPowerOf 2u; 2 inPowerOf 3u } // 5 x_0^2 x_2^3 +
|
||||
* (-6) { 1 inPowerOf 1u } // (-6) x_1^1
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @usesMathJax
|
||||
*/
|
||||
@DslMarker
|
||||
@UnstableKMathAPI
|
||||
internal annotation class NumberedPolynomialConstructorDSL1
|
||||
|
||||
/**
|
||||
* Builder of [NumberedPolynomial] signature. It should be used as an implicit context for lambdas that describe term signature.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
@NumberedPolynomialConstructorDSL1
|
||||
public class DSL1NumberedPolynomialTermSignatureBuilder {
|
||||
/**
|
||||
* Signature storage. Any declaration of any variable's power updates the storage by increasing corresponding value.
|
||||
* Afterward the storage will be used as a resulting signature.
|
||||
*/
|
||||
private val signature: MutableList<UInt> = ArrayList()
|
||||
|
||||
/**
|
||||
* Builds the resulting signature.
|
||||
*
|
||||
* In fact, it just returns [signature] as regular signature of type `List<UInt>`.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun build(): List<UInt> = signature
|
||||
|
||||
/**
|
||||
* Declares power of variable #[this] of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public infix fun Int.inPowerOf(deg: UInt) {
|
||||
if (deg == 0u) return
|
||||
val index = this
|
||||
if (index > signature.lastIndex) {
|
||||
signature.addAll(List(index - signature.lastIndex - 1) { 0u })
|
||||
signature.add(deg)
|
||||
} else {
|
||||
signature[index] += deg
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Declares power of variable #[this] of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public inline infix fun Int.pow(deg: UInt): Unit = this inPowerOf deg
|
||||
/**
|
||||
* Declares power of variable #[this] of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public inline infix fun Int.`in`(deg: UInt): Unit = this inPowerOf deg
|
||||
/**
|
||||
* Declares power of variable #[this] of degree [deg].
|
||||
*
|
||||
* Declaring another power of the same variable will increase its degree by received degree.
|
||||
*/
|
||||
public inline infix fun Int.of(deg: UInt): Unit = this inPowerOf deg
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder of [NumberedPolynomial]. It should be used as an implicit context for lambdas that describe [NumberedPolynomial].
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
@NumberedPolynomialConstructorDSL1
|
||||
public class DSL1NumberedPolynomialBuilder<C>(
|
||||
/**
|
||||
* Summation operation that will be used to sum coefficients of monomials of same signatures.
|
||||
*/
|
||||
private val add: (C, C) -> C,
|
||||
/**
|
||||
* Initial capacity of coefficients map.
|
||||
*/
|
||||
initialCapacity: Int? = null
|
||||
) {
|
||||
/**
|
||||
* Coefficients storage. Any declaration of any monomial updates the storage.
|
||||
* Afterward the storage will be used as a resulting coefficients map.
|
||||
*/
|
||||
private val coefficients: MutableMap<List<UInt>, C> = if (initialCapacity != null) LinkedHashMap(initialCapacity) else LinkedHashMap()
|
||||
|
||||
/**
|
||||
* Builds the resulting coefficients map.
|
||||
*
|
||||
* In fact, it just returns [coefficients] as regular coefficients map of type `Map<List<UInt>, C>`.
|
||||
*/
|
||||
@PublishedApi
|
||||
internal fun build(): NumberedPolynomial<C> = NumberedPolynomial<C>(coefficients)
|
||||
|
||||
/**
|
||||
* Declares monomial with [this] coefficient and provided [signature].
|
||||
*
|
||||
* Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such
|
||||
* coefficients is zero at any moment the monomial won't be removed but will be left as it is.
|
||||
*/
|
||||
public infix fun C.with(signature: List<UInt>) {
|
||||
coefficients.putOrChange(signature, this@with, add)
|
||||
}
|
||||
/**
|
||||
* Declares monomial with [this] coefficient and signature constructed by [block].
|
||||
*
|
||||
* Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such
|
||||
* coefficients is zero at any moment the monomial won't be removed but will be left as it is.
|
||||
*/
|
||||
public inline infix fun C.with(noinline block: DSL1NumberedPolynomialTermSignatureBuilder.() -> Unit): Unit = this.invoke(block)
|
||||
/**
|
||||
* Declares monomial with [this] coefficient and signature constructed by [block].
|
||||
*
|
||||
* Declaring another monomial with the same signature will add [this] coefficient to existing one. If the sum of such
|
||||
* coefficients is zero at any moment the monomial won't be removed but will be left as it is.
|
||||
*/
|
||||
public inline operator fun C.invoke(block: DSL1NumberedPolynomialTermSignatureBuilder.() -> Unit): Unit =
|
||||
this with DSL1NumberedPolynomialTermSignatureBuilder().apply(block).build()
|
||||
}
|
||||
|
||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
||||
|
||||
///**
|
||||
// * Creates [NumberedPolynomial] with lambda [block] in context of [this] ring of constants.
|
||||
// *
|
||||
// * For example, polynomial \(5 x_0^2 x_2^3 - 6 x_1\) can be described as
|
||||
// * ```
|
||||
// * Int.algebra {
|
||||
// * val numberedPolynomial : NumberedPolynomial<Int> = NumberedPolynomial {
|
||||
// * 5 { 0 inPowerOf 2u; 2 inPowerOf 3u } // 5 x_0^2 x_2^3 +
|
||||
// * (-6) { 1 inPowerOf 1u } // (-6) x_1^1
|
||||
// * }
|
||||
// * }
|
||||
// * ```
|
||||
// * @usesMathJax
|
||||
// */
|
||||
// FIXME: For now this fabric does not let next two fabrics work. (See KT-52803.) Possible feature solutions:
|
||||
// 1. `LowPriorityInOverloadResolution` becomes public. Then it should be applied to this function.
|
||||
// 2. Union types are implemented. Then all three functions should be rewritten
|
||||
// as one with single union type as a (context) receiver.
|
||||
//@UnstableKMathAPI
|
||||
//public inline fun <C, A: Ring<C>> A.NumberedPolynomialDSL1(initialCapacity: Int? = null, block: NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = NumberedPolynomialBuilder(::add, initialCapacity).apply(block).build()
|
||||
/**
|
||||
* Creates [NumberedPolynomial] with lambda [block] in context of [this] ring of [NumberedPolynomial]s.
|
||||
*
|
||||
* For example, polynomial \(5 x_0^2 x_2^3 - 6 x_1\) can be described as
|
||||
* ```
|
||||
* Int.algebra {
|
||||
* val numberedPolynomial : NumberedPolynomial<Int> = NumberedPolynomial {
|
||||
* 5 { 0 inPowerOf 2u; 2 inPowerOf 3u } // 5 x_0^2 x_2^3 +
|
||||
* (-6) { 1 inPowerOf 1u } // (-6) x_1^1
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @usesMathJax
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public inline fun <C, A: Ring<C>> NumberedPolynomialSpace<C, A>.NumberedPolynomialDSL1(initialCapacity: Int? = null, block: DSL1NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = DSL1NumberedPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build()
|
||||
/**
|
||||
* Creates [NumberedPolynomial] with lambda [block] in context of [this] field of [NumberedRationalFunction]s.
|
||||
*
|
||||
* For example, polynomial \(5 x_0^2 x_2^3 - 6 x_1\) can be described as
|
||||
* ```
|
||||
* Int.algebra {
|
||||
* val numberedPolynomial : NumberedPolynomial<Int> = NumberedPolynomial {
|
||||
* 5 { 0 inPowerOf 2u; 2 inPowerOf 3u } // 5 x_0^2 x_2^3 +
|
||||
* (-6) { 1 inPowerOf 1u } // (-6) x_1^1
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @usesMathJax
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public inline fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedPolynomialDSL1(initialCapacity: Int? = null, block: DSL1NumberedPolynomialBuilder<C>.() -> Unit) : NumberedPolynomial<C> = DSL1NumberedPolynomialBuilder({ left: C, right: C -> left + right }, initialCapacity).apply(block).build()
|
||||
|
||||
// Waiting for context receivers :( FIXME: Replace with context receivers when they will be available
|
||||
|
||||
/**
|
||||
* Constructs [NumberedRationalFunction] with provided coefficients maps [numeratorCoefficients] and [denominatorCoefficients].
|
||||
*
|
||||
* The maps will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. the maps' keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>, denominatorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction<C>(
|
||||
NumberedPolynomial(numeratorCoefficients),
|
||||
NumberedPolynomial(denominatorCoefficients)
|
||||
)
|
||||
/**
|
||||
* Constructs [NumberedRationalFunction] with provided coefficients maps [numeratorCoefficients] and [denominatorCoefficients].
|
||||
*
|
||||
* The maps will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. the maps' keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>, denominatorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction<C>(
|
||||
NumberedPolynomial(numeratorCoefficients),
|
||||
NumberedPolynomial(denominatorCoefficients)
|
||||
)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedRationalFunction] with provided [numerator] and unit denominator.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction<C>(numerator, NumberedPolynomial(mapOf(emptyList<UInt>() to one)))
|
||||
/**
|
||||
* Constructs [NumberedRationalFunction] with provided [numerator] and unit denominator.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numerator: NumberedPolynomial<C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction<C>(numerator, polynomialOne)
|
||||
|
||||
/**
|
||||
* Constructs [NumberedRationalFunction] with provided coefficients map [numeratorCoefficients] for numerator and unit
|
||||
* denominator.
|
||||
*
|
||||
* [numeratorCoefficients] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [numeratorCoefficients]'s keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> NumberedRationalFunctionSpace<C, A>.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction<C>(
|
||||
NumberedPolynomial(numeratorCoefficients),
|
||||
polynomialOne
|
||||
)
|
||||
/**
|
||||
* Constructs [NumberedRationalFunction] with provided coefficients map [numeratorCoefficients] for numerator and unit
|
||||
* denominator.
|
||||
*
|
||||
* [numeratorCoefficients] will be "cleaned up":
|
||||
* 1. Zeros at the ends of terms' signatures (e.g. [numeratorCoefficients]'s keys) will be removed. (See [cleanUp].)
|
||||
* 1. Terms that happen to have the same signature will be summed up.
|
||||
* 1. New map will be formed of resulting terms.
|
||||
*/
|
||||
public fun <C, A: Ring<C>> A.NumberedRationalFunction(numeratorCoefficients: Map<List<UInt>, C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction<C>(
|
||||
NumberedPolynomial(numeratorCoefficients),
|
||||
NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to one))
|
||||
)
|
||||
|
||||
///**
|
||||
// * Converts [this] constant to [NumberedRationalFunction].
|
||||
// */
|
||||
//context(A)
|
||||
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> =
|
||||
// NumberedRationalFunction(
|
||||
// NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to this)),
|
||||
// NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to one))
|
||||
// )
|
||||
///**
|
||||
// * Converts [this] constant to [NumberedRationalFunction].
|
||||
// */
|
||||
//context(NumberedRationalFunctionSpace<C, A>)
|
||||
//public fun <C, A: Ring<C>> C.asNumberedRationalFunction() : NumberedRationalFunction<C> =
|
||||
// NumberedRationalFunction(
|
||||
// NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to this)),
|
||||
// NumberedPolynomialAsIs(mapOf(emptyList<UInt>() to constantOne))
|
||||
// )
|
@ -0,0 +1,513 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.Field
|
||||
import space.kscience.kmath.operations.Ring
|
||||
import space.kscience.kmath.operations.algebra
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
/**
|
||||
* Creates a [NumberedPolynomialSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A : Ring<C>> A.numberedPolynomialSpace: NumberedPolynomialSpace<C, A>
|
||||
get() = NumberedPolynomialSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [NumberedPolynomialSpace]'s scope over a received ring.
|
||||
*/
|
||||
public inline fun <C, A : Ring<C>, R> A.numberedPolynomialSpace(block: NumberedPolynomialSpace<C, A>.() -> R): R {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return NumberedPolynomialSpace(this).block()
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [NumberedRationalFunctionSpace] over a received ring.
|
||||
*/
|
||||
public inline val <C, A : Ring<C>> A.numberedRationalFunctionSpace: NumberedRationalFunctionSpace<C, A>
|
||||
get() = NumberedRationalFunctionSpace(this)
|
||||
|
||||
/**
|
||||
* Creates a [NumberedRationalFunctionSpace]'s scope over a received ring.
|
||||
*/
|
||||
public inline fun <C, A : Ring<C>, R> A.numberedRationalFunctionSpace(block: NumberedRationalFunctionSpace<C, A>.() -> R): R {
|
||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||
return NumberedRationalFunctionSpace(this).block()
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double polynomial.
|
||||
*/
|
||||
public fun NumberedPolynomial<Double>.substitute(args: Map<Int, Double>): NumberedPolynomial<Double> = Double.algebra {
|
||||
NumberedPolynomial<Double>(
|
||||
buildMap(coefficients.size) {
|
||||
for ((degs, c) in coefficients) {
|
||||
val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp()
|
||||
val newC = args.entries.fold(c) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * substitution.pow(deg.toInt())
|
||||
}
|
||||
putOrChange(newDegs, newC) { it -> it + newC }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/
|
||||
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, C>): NumberedPolynomial<C> = ring {
|
||||
NumberedPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
for ((degs, c) in coefficients) {
|
||||
val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp()
|
||||
val newC = args.entries.fold(c) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * power(substitution, deg)
|
||||
}
|
||||
putOrChange(newDegs, newC) { it -> it + newC }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/ // TODO: To optimize boxing
|
||||
@JvmName("substitutePolynomial")
|
||||
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedPolynomial<C>>) : NumberedPolynomial<C> =
|
||||
ring.numberedPolynomialSpace {
|
||||
coefficients.entries.fold(zero) { acc, (degs, c) ->
|
||||
val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp()
|
||||
acc + args.entries.fold(NumberedPolynomial<C>(mapOf(newDegs to c))) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * power(substitution, deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/ // TODO: To optimize boxing
|
||||
@JvmName("substituteRationalFunction")
|
||||
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedRationalFunction<C>>) : NumberedRationalFunction<C> =
|
||||
ring.numberedRationalFunctionSpace {
|
||||
coefficients.entries.fold(zero) { acc, (degs, c) ->
|
||||
val newDegs = degs.mapIndexed { index, deg -> if (index !in args) deg else 0u }.cleanUp()
|
||||
acc + args.entries.fold(NumberedRationalFunction(NumberedPolynomial<C>(mapOf(newDegs to c)))) { product, (variable, substitution) ->
|
||||
val deg = degs.getOrElse(variable) { 0u }
|
||||
if (deg == 0u) product else product * power(substitution, deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double rational function.
|
||||
*/
|
||||
public fun NumberedRationalFunction<Double>.substitute(args: Map<Int, Double>): NumberedRationalFunction<Double> =
|
||||
NumberedRationalFunction(numerator.substitute(args), denominator.substitute(args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/
|
||||
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Int, C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/ // TODO: To optimize calculation
|
||||
@JvmName("substitutePolynomial")
|
||||
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedPolynomial<C>>) : NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/ // TODO: To optimize calculation
|
||||
@JvmName("substituteRationalFunction")
|
||||
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Map<Int, NumberedRationalFunction<C>>) : NumberedRationalFunction<C> =
|
||||
ring.numberedRationalFunctionSpace {
|
||||
numerator.substitute(ring, args) / denominator.substitute(ring, args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double polynomial.
|
||||
*/
|
||||
public fun NumberedPolynomial<Double>.substitute(args: Buffer<Double>): NumberedPolynomial<Double> = Double.algebra {
|
||||
val lastSubstitutionVariable = args.size - 1
|
||||
NumberedPolynomial<Double>(
|
||||
buildMap(coefficients.size) {
|
||||
for ((degs, c) in coefficients) {
|
||||
val lastDegsIndex = degs.lastIndex
|
||||
val newDegs =
|
||||
if (lastDegsIndex <= lastSubstitutionVariable) emptyList()
|
||||
else degs.toMutableList().apply {
|
||||
for (i in 0..lastSubstitutionVariable) this[i] = 0u
|
||||
}
|
||||
val newC = (0..min(lastDegsIndex, lastSubstitutionVariable)).fold(c) { product, variable ->
|
||||
val deg = degs[variable]
|
||||
if (deg == 0u) product else product * args[variable].pow(deg.toInt())
|
||||
}
|
||||
putOrChange(newDegs, newC) { it -> it + newC }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/
|
||||
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Buffer<C>): NumberedPolynomial<C> = ring {
|
||||
val lastSubstitutionVariable = args.size - 1
|
||||
NumberedPolynomial<C>(
|
||||
buildMap<List<UInt>, C>(coefficients.size) {
|
||||
for ((degs, c) in coefficients) {
|
||||
val lastDegsIndex = degs.lastIndex
|
||||
val newDegs =
|
||||
if (lastDegsIndex <= lastSubstitutionVariable) emptyList()
|
||||
else degs.toMutableList().apply {
|
||||
for (i in 0..lastSubstitutionVariable) this[i] = 0u
|
||||
}
|
||||
val newC = (0..min(lastDegsIndex, lastSubstitutionVariable)).fold(c) { product, variable ->
|
||||
val deg = degs[variable]
|
||||
if (deg == 0u) product else product * power(args[variable], deg)
|
||||
}
|
||||
putOrChange(newDegs, newC) { it -> it + newC }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/ // TODO: To optimize boxing
|
||||
@JvmName("substitutePolynomial")
|
||||
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Buffer<NumberedPolynomial<C>>) : NumberedPolynomial<C> =
|
||||
ring.numberedPolynomialSpace {
|
||||
val lastSubstitutionVariable = args.size - 1
|
||||
coefficients.entries.fold(zero) { acc, (degs, c) ->
|
||||
val lastDegsIndex = degs.lastIndex
|
||||
val newDegs =
|
||||
if (lastDegsIndex <= lastSubstitutionVariable) emptyList()
|
||||
else degs.toMutableList().apply {
|
||||
for (i in 0..lastSubstitutionVariable) this[i] = 0u
|
||||
}
|
||||
acc + (0..min(lastDegsIndex, lastSubstitutionVariable))
|
||||
.fold(NumberedPolynomial<C>(mapOf(newDegs to c))) { product, variable ->
|
||||
val deg = degs[variable]
|
||||
if (deg == 0u) product else product * power(args[variable], deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/ // TODO: To optimize boxing
|
||||
@JvmName("substituteRationalFunction")
|
||||
public fun <C> NumberedPolynomial<C>.substitute(ring: Ring<C>, args: Buffer<NumberedRationalFunction<C>>) : NumberedRationalFunction<C> =
|
||||
ring.numberedRationalFunctionSpace {
|
||||
val lastSubstitutionVariable = args.size - 1
|
||||
coefficients.entries.fold(zero) { acc, (degs, c) ->
|
||||
val lastDegsIndex = degs.lastIndex
|
||||
val newDegs =
|
||||
if (lastDegsIndex <= lastSubstitutionVariable) emptyList()
|
||||
else degs.toMutableList().apply {
|
||||
for (i in 0..lastSubstitutionVariable) this[i] = 0u
|
||||
}
|
||||
acc + (0..min(lastDegsIndex, lastSubstitutionVariable))
|
||||
.fold(NumberedRationalFunction(NumberedPolynomial<C>(mapOf(newDegs to c)))) { product, variable ->
|
||||
val deg = degs[variable]
|
||||
if (deg == 0u) product else product * power(args[variable], deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double rational function.
|
||||
*/
|
||||
public fun NumberedRationalFunction<Double>.substitute(args: Buffer<Double>): NumberedRationalFunction<Double> =
|
||||
NumberedRationalFunction(numerator.substitute(args), denominator.substitute(args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/
|
||||
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Buffer<C>): NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/ // TODO: To optimize calculation
|
||||
@JvmName("substitutePolynomial")
|
||||
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Buffer<NumberedPolynomial<C>>) : NumberedRationalFunction<C> =
|
||||
NumberedRationalFunction(numerator.substitute(ring, args), denominator.substitute(ring, args))
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/ // TODO: To optimize calculation
|
||||
@JvmName("substituteRationalFunction")
|
||||
public fun <C> NumberedRationalFunction<C>.substitute(ring: Ring<C>, args: Buffer<NumberedRationalFunction<C>>) : NumberedRationalFunction<C> =
|
||||
ring.numberedRationalFunctionSpace {
|
||||
numerator.substitute(ring, args) / denominator.substitute(ring, args)
|
||||
}
|
||||
|
||||
internal const val fullSubstitutionExceptionMessage: String = "Fully substituting buffer should cover all variables of the polynomial."
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double polynomial.
|
||||
*/
|
||||
public fun NumberedPolynomial<Double>.substituteFully(args: Buffer<Double>): Double = Double.algebra {
|
||||
val lastSubstitutionVariable = args.size - 1
|
||||
require(coefficients.keys.all { it.lastIndex <= lastSubstitutionVariable }) { fullSubstitutionExceptionMessage }
|
||||
coefficients.entries.fold(.0) { acc, (degs, c) ->
|
||||
acc + degs.foldIndexed(c) { variable, product, deg ->
|
||||
if (deg == 0u) product else product * args[variable].pow(deg.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] polynomial.
|
||||
*/
|
||||
public fun <C> NumberedPolynomial<C>.substituteFully(ring: Ring<C>, args: Buffer<C>): C = ring {
|
||||
val lastSubstitutionVariable = args.size - 1
|
||||
require(coefficients.keys.all { it.lastIndex <= lastSubstitutionVariable }) { fullSubstitutionExceptionMessage }
|
||||
coefficients.entries.fold(zero) { acc, (degs, c) ->
|
||||
acc + degs.foldIndexed(c) { variable, product, deg ->
|
||||
if (deg == 0u) product else product * power(args[variable], deg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes provided Double arguments [args] into [this] Double rational function.
|
||||
*/
|
||||
public fun NumberedRationalFunction<Double>.substituteFully(args: Buffer<Double>): Double =
|
||||
numerator.substituteFully(args) / denominator.substituteFully(args)
|
||||
|
||||
/**
|
||||
* Substitutes provided arguments [args] into [this] rational function.
|
||||
*/
|
||||
public fun <C> NumberedRationalFunction<C>.substituteFully(ring: Field<C>, args: Buffer<C>): C = ring {
|
||||
numerator.substituteFully(ring, args) / denominator.substituteFully(ring, args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunctionOver(ring: A): (Buffer<C>) -> C = { substituteFully(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunctionOfConstantOver(ring: A): (Buffer<C>) -> C = { substituteFully(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunctionOfPolynomialOver(ring: A): (Buffer<NumberedPolynomial<C>>) -> NumberedPolynomial<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] polynomial as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.asFunctionOfRationalFunctionOver(ring: A): (Buffer<NumberedRationalFunction<C>>) -> NumberedRationalFunction<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Field<C>> NumberedRationalFunction<C>.asFunctionOver(ring: A): (Buffer<C>) -> C = { substituteFully(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Field<C>> NumberedRationalFunction<C>.asFunctionOfConstantOver(ring: A): (Buffer<C>) -> C = { substituteFully(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> NumberedRationalFunction<C>.asFunctionOfPolynomialOver(ring: A): (Buffer<NumberedPolynomial<C>>) -> NumberedRationalFunction<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Represent [this] rational function as a regular context-less function.
|
||||
*/
|
||||
public fun <C, A : Ring<C>> NumberedRationalFunction<C>.asFunctionOfRationalFunctionOver(ring: A): (Buffer<NumberedRationalFunction<C>>) -> NumberedRationalFunction<C> = { substitute(ring, it) }
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variable.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.derivativeWithRespectTo(
|
||||
ring: A,
|
||||
variable: Int,
|
||||
): NumberedPolynomial<C> = ring {
|
||||
NumberedPolynomial<C>(
|
||||
buildMap(coefficients.count { it.key.getOrElse(variable) { 0u } >= 1u }) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
if (degs.lastIndex < variable) return@forEach
|
||||
put(
|
||||
degs.mapIndexed { index, deg ->
|
||||
when {
|
||||
index != variable -> deg
|
||||
deg > 0u -> deg - 1u
|
||||
else -> return@forEach
|
||||
}
|
||||
}.cleanUp(),
|
||||
multiplyByDoubling(c, degs[variable])
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variable of specified order.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.nthDerivativeWithRespectTo(
|
||||
ring: A,
|
||||
variable: Int,
|
||||
order: UInt
|
||||
): NumberedPolynomial<C> = ring {
|
||||
if (order == 0u) return this@nthDerivativeWithRespectTo
|
||||
NumberedPolynomial<C>(
|
||||
buildMap(coefficients.count { it.key.getOrElse(variable) { 0u } >= order }) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
if (degs.lastIndex < variable) return@forEach
|
||||
put(
|
||||
degs.mapIndexed { index, deg ->
|
||||
when {
|
||||
index != variable -> deg
|
||||
deg >= order -> deg - order
|
||||
else -> return@forEach
|
||||
}
|
||||
}.cleanUp(),
|
||||
degs[variable].let { deg ->
|
||||
(deg downTo deg - order + 1u)
|
||||
.fold(c) { acc, ord -> multiplyByDoubling(acc, ord) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variables of specified orders.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Ring<C>> NumberedPolynomial<C>.nthDerivativeWithRespectTo(
|
||||
ring: A,
|
||||
variablesAndOrders: Map<Int, UInt>,
|
||||
): NumberedPolynomial<C> = ring {
|
||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthDerivativeWithRespectTo
|
||||
val maxRespectedVariable = filteredVariablesAndOrders.keys.maxOrNull()!!
|
||||
NumberedPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
if (degs.lastIndex < maxRespectedVariable) return@forEach
|
||||
put(
|
||||
degs.mapIndexed { index, deg ->
|
||||
if (index !in filteredVariablesAndOrders) return@mapIndexed deg
|
||||
val order = filteredVariablesAndOrders[index]!!
|
||||
if (deg >= order) deg - order else return@forEach
|
||||
}.cleanUp(),
|
||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (index, order) ->
|
||||
degs[index].let { deg ->
|
||||
(deg downTo deg - order + 1u)
|
||||
.fold(acc1) { acc2, ord -> multiplyByDoubling(acc2, ord) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic antiderivative of received polynomial with respect to provided variable.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Field<C>> NumberedPolynomial<C>.antiderivativeWithRespectTo(
|
||||
ring: A,
|
||||
variable: Int,
|
||||
): NumberedPolynomial<C> = ring {
|
||||
NumberedPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
put(
|
||||
List(max(variable + 1, degs.size)) { degs.getOrElse(it) { 0u } + if (it != variable) 0u else 1u },
|
||||
c / multiplyByDoubling(one, degs.getOrElse(variable) { 0u } + 1u)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic antiderivative of received polynomial with respect to provided variable of specified order.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Field<C>> NumberedPolynomial<C>.nthAntiderivativeWithRespectTo(
|
||||
ring: A,
|
||||
variable: Int,
|
||||
order: UInt
|
||||
): NumberedPolynomial<C> = ring {
|
||||
if (order == 0u) return this@nthAntiderivativeWithRespectTo
|
||||
NumberedPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
put(
|
||||
List(max(variable + 1, degs.size)) { degs.getOrElse(it) { 0u } + if (it != variable) 0u else order },
|
||||
degs.getOrElse(variable) { 0u }.let { deg ->
|
||||
(deg + 1u .. deg + order)
|
||||
.fold(c) { acc, ord -> acc / multiplyByDoubling(one, ord) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns algebraic derivative of received polynomial with respect to provided variables of specified orders.
|
||||
*/
|
||||
@UnstableKMathAPI
|
||||
public fun <C, A : Field<C>> NumberedPolynomial<C>.nthAntiderivativeWithRespectTo(
|
||||
ring: A,
|
||||
variablesAndOrders: Map<Int, UInt>,
|
||||
): NumberedPolynomial<C> = ring {
|
||||
val filteredVariablesAndOrders = variablesAndOrders.filterValues { it != 0u }
|
||||
if (filteredVariablesAndOrders.isEmpty()) return this@nthAntiderivativeWithRespectTo
|
||||
val maxRespectedVariable = filteredVariablesAndOrders.keys.maxOrNull()!!
|
||||
NumberedPolynomial<C>(
|
||||
buildMap(coefficients.size) {
|
||||
coefficients
|
||||
.forEach { (degs, c) ->
|
||||
put(
|
||||
List(max(maxRespectedVariable + 1, degs.size)) { degs.getOrElse(it) { 0u } + filteredVariablesAndOrders.getOrElse(it) { 0u } },
|
||||
filteredVariablesAndOrders.entries.fold(c) { acc1, (variable, order) ->
|
||||
degs.getOrElse(variable) { 0u }.let { deg ->
|
||||
(deg + 1u .. deg + order)
|
||||
.fold(acc1) { acc, ord -> acc / multiplyByDoubling(one, ord) }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,589 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import space.kscience.kmath.operations.Field
|
||||
import kotlin.jvm.JvmInline
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@JvmInline
|
||||
value class Expr(val expr: String)
|
||||
|
||||
object ExprRing : Field<Expr> {
|
||||
override fun Expr.unaryMinus(): Expr = Expr("-${expr}")
|
||||
override fun add(left: Expr, right: Expr): Expr = Expr("(${left.expr} + ${right.expr})")
|
||||
override fun multiply(left: Expr, right: Expr): Expr = Expr("(${left.expr} * ${right.expr})")
|
||||
override val zero: Expr = Expr("0")
|
||||
override val one: Expr = Expr("1")
|
||||
override fun divide(left: Expr, right: Expr): Expr = Expr("(${left.expr} / ${right.expr})")
|
||||
override fun scale(a: Expr, value: Double): Expr = Expr("(${a.expr} / $value)")
|
||||
}
|
||||
|
||||
class AlgebraicStubTest {
|
||||
@Test
|
||||
fun test_addMultipliedBySquaring_for_UInt() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"57",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 0u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 0u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + 179)",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 1u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 1u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (179 + 179))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 2u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 2u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 179) + (179 + 179))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 3u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 3u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 4u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 4u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 179) + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 5u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 5u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + (179 + 179)) + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 6u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 6u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 + 179) + (179 + 179)) + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 7u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 7u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (((179 + 179) + (179 + 179)) + ((179 + 179) + (179 + 179))))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 8u).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 8u)"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_multiplyBySquaring_for_UInt() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"0",
|
||||
multiplyByDoubling(Expr("57"), 0u).expr,
|
||||
"tried multiplyBySquaring(57, 0u)"
|
||||
)
|
||||
assertEquals(
|
||||
"57",
|
||||
multiplyByDoubling(Expr("57"), 1u).expr,
|
||||
"tried multiplyBySquaring(57, 1u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + 57)",
|
||||
multiplyByDoubling(Expr("57"), 2u).expr,
|
||||
"tried multiplyBySquaring(57, 2u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (57 + 57))",
|
||||
multiplyByDoubling(Expr("57"), 3u).expr,
|
||||
"tried multiplyBySquaring(57, 3u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 57) + (57 + 57))",
|
||||
multiplyByDoubling(Expr("57"), 4u).expr,
|
||||
"tried multiplyBySquaring(57, 4u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 5u).expr,
|
||||
"tried multiplyBySquaring(57, 5u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 57) + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 6u).expr,
|
||||
"tried multiplyBySquaring(57, 6u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + (57 + 57)) + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 7u).expr,
|
||||
"tried multiplyBySquaring(57, 7u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 + 57) + (57 + 57)) + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 8u).expr,
|
||||
"tried multiplyBySquaring(57, 8u)"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_addMultipliedBySquaring_for_Int() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"57",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 0).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 0)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + 179)",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 1).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 1)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + -179)",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -1).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -1)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (179 + 179))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 2).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 2)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (-179 + -179))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -2).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -2)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 179) + (179 + 179))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 3).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 3)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + -179) + (-179 + -179))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -3).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -3)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 4).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 4)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + ((-179 + -179) + (-179 + -179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -4).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -4)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 179) + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 5).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 5)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + -179) + ((-179 + -179) + (-179 + -179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -5).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -5)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + (179 + 179)) + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 6).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 6)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + (-179 + -179)) + ((-179 + -179) + (-179 + -179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -6).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -6)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 + 179) + (179 + 179)) + ((179 + 179) + (179 + 179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 7).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 7)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 + -179) + (-179 + -179)) + ((-179 + -179) + (-179 + -179)))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -7).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -7)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (((179 + 179) + (179 + 179)) + ((179 + 179) + (179 + 179))))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), 8).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, 8)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (((-179 + -179) + (-179 + -179)) + ((-179 + -179) + (-179 + -179))))",
|
||||
addMultipliedByDoubling(Expr("57"), Expr("179"), -8).expr,
|
||||
"tried addMultipliedBySquaring(57, 179, -8)"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_multiplyBySquaring_for_Int() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"0",
|
||||
multiplyByDoubling(Expr("57"), 0).expr,
|
||||
"tried multiplyBySquaring(57, 0)"
|
||||
)
|
||||
assertEquals(
|
||||
"57",
|
||||
multiplyByDoubling(Expr("57"), 1).expr,
|
||||
"tried multiplyBySquaring(57, 1)"
|
||||
)
|
||||
assertEquals(
|
||||
"-57",
|
||||
multiplyByDoubling(Expr("57"), -1).expr,
|
||||
"tried multiplyBySquaring(57, -1)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + 57)",
|
||||
multiplyByDoubling(Expr("57"), 2).expr,
|
||||
"tried multiplyBySquaring(57, 2)"
|
||||
)
|
||||
assertEquals(
|
||||
"(-57 + -57)",
|
||||
multiplyByDoubling(Expr("57"), -2).expr,
|
||||
"tried multiplyBySquaring(57, -2)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + (57 + 57))",
|
||||
multiplyByDoubling(Expr("57"), 3).expr,
|
||||
"tried multiplyBySquaring(57, 3)"
|
||||
)
|
||||
assertEquals(
|
||||
"(-57 + (-57 + -57))",
|
||||
multiplyByDoubling(Expr("57"), -3).expr,
|
||||
"tried multiplyBySquaring(57, -3)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 57) + (57 + 57))",
|
||||
multiplyByDoubling(Expr("57"), 4).expr,
|
||||
"tried multiplyBySquaring(57, 4)"
|
||||
)
|
||||
assertEquals(
|
||||
"((-57 + -57) + (-57 + -57))",
|
||||
multiplyByDoubling(Expr("57"), -4).expr,
|
||||
"tried multiplyBySquaring(57, -4)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 5).expr,
|
||||
"tried multiplyBySquaring(57, 5)"
|
||||
)
|
||||
assertEquals(
|
||||
"(-57 + ((-57 + -57) + (-57 + -57)))",
|
||||
multiplyByDoubling(Expr("57"), -5).expr,
|
||||
"tried multiplyBySquaring(57, -5)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + 57) + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 6).expr,
|
||||
"tried multiplyBySquaring(57, 6)"
|
||||
)
|
||||
assertEquals(
|
||||
"((-57 + -57) + ((-57 + -57) + (-57 + -57)))",
|
||||
multiplyByDoubling(Expr("57"), -6).expr,
|
||||
"tried multiplyBySquaring(57, -6)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 + (57 + 57)) + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 7).expr,
|
||||
"tried multiplyBySquaring(57, 7)"
|
||||
)
|
||||
assertEquals(
|
||||
"((-57 + (-57 + -57)) + ((-57 + -57) + (-57 + -57)))",
|
||||
multiplyByDoubling(Expr("57"), -7).expr,
|
||||
"tried multiplyBySquaring(57, -7)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 + 57) + (57 + 57)) + ((57 + 57) + (57 + 57)))",
|
||||
multiplyByDoubling(Expr("57"), 8).expr,
|
||||
"tried multiplyBySquaring(57, 8)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((-57 + -57) + (-57 + -57)) + ((-57 + -57) + (-57 + -57)))",
|
||||
multiplyByDoubling(Expr("57"), -8).expr,
|
||||
"tried multiplyBySquaring(57, -8)"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_multiplyExponentiationBySquaring_for_UInt() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"57",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 0u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 0u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * 179)",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 1u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 1u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (179 * 179))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 2u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 2u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 179) * (179 * 179))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 3u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 3u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 4u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 4u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 179) * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 5u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 5u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * (179 * 179)) * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 6u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 6u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 * 179) * (179 * 179)) * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 7u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 7u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (((179 * 179) * (179 * 179)) * ((179 * 179) * (179 * 179))))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 8u).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 8u)"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_exponentiationBySquaring_for_UInt() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"0",
|
||||
exponentiateBySquaring(Expr("57"), 0u).expr,
|
||||
"tried exponentiationBySquaring(57, 0u)"
|
||||
)
|
||||
assertEquals(
|
||||
"57",
|
||||
exponentiateBySquaring(Expr("57"), 1u).expr,
|
||||
"tried exponentiationBySquaring(57, 1u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * 57)",
|
||||
exponentiateBySquaring(Expr("57"), 2u).expr,
|
||||
"tried exponentiationBySquaring(57, 2u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (57 * 57))",
|
||||
exponentiateBySquaring(Expr("57"), 3u).expr,
|
||||
"tried exponentiationBySquaring(57, 3u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 57) * (57 * 57))",
|
||||
exponentiateBySquaring(Expr("57"), 4u).expr,
|
||||
"tried exponentiationBySquaring(57, 4u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 5u).expr,
|
||||
"tried exponentiationBySquaring(57, 5u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 57) * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 6u).expr,
|
||||
"tried exponentiationBySquaring(57, 6u)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * (57 * 57)) * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 7u).expr,
|
||||
"tried exponentiationBySquaring(57, 7u)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 * 57) * (57 * 57)) * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 8u).expr,
|
||||
"tried exponentiationBySquaring(57, 8u)"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_multiplyExponentiationBySquaring_for_Int() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"57",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 0).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 0)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * 179)",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 1).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 1)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (1 / 179))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -1).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -1)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (179 * 179))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 2).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 2)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * ((1 / 179) * (1 / 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -2).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -2)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 179) * (179 * 179))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 3).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 3)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * (1 / 179)) * ((1 / 179) * (1 / 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -3).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -3)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 4).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 4)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (((1 / 179) * (1 / 179)) * ((1 / 179) * (1 / 179))))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -4).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -4)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 179) * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 5).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 5)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * (1 / 179)) * (((1 / 179) * (1 / 179)) * ((1 / 179) * (1 / 179))))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -5).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -5)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * (179 * 179)) * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 6).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 6)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * ((1 / 179) * (1 / 179))) * (((1 / 179) * (1 / 179)) * ((1 / 179) * (1 / 179))))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -6).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -6)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 * 179) * (179 * 179)) * ((179 * 179) * (179 * 179)))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 7).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 7)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 * (1 / 179)) * ((1 / 179) * (1 / 179))) * (((1 / 179) * (1 / 179)) * ((1 / 179) * (1 / 179))))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -7).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -7)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (((179 * 179) * (179 * 179)) * ((179 * 179) * (179 * 179))))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), 8).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, 8)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * ((((1 / 179) * (1 / 179)) * ((1 / 179) * (1 / 179))) * (((1 / 179) * (1 / 179)) * ((1 / 179) * (1 / 179)))))",
|
||||
multiplyExponentiatedBySquaring(Expr("57"), Expr("179"), -8).expr,
|
||||
"tried multiplyExponentiationBySquaring(57, 179, -8)"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_exponentiationBySquaring_for_Int() {
|
||||
ExprRing {
|
||||
assertEquals(
|
||||
"0",
|
||||
exponentiateBySquaring(Expr("57"), 0).expr,
|
||||
"tried exponentiationBySquaring(57, 0)"
|
||||
)
|
||||
assertEquals(
|
||||
"57",
|
||||
exponentiateBySquaring(Expr("57"), 1).expr,
|
||||
"tried exponentiationBySquaring(57, 1)"
|
||||
)
|
||||
assertEquals(
|
||||
"(1 / 57)",
|
||||
exponentiateBySquaring(Expr("57"), -1).expr,
|
||||
"tried exponentiationBySquaring(57, -1)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * 57)",
|
||||
exponentiateBySquaring(Expr("57"), 2).expr,
|
||||
"tried exponentiationBySquaring(57, 2)"
|
||||
)
|
||||
assertEquals(
|
||||
"((1 / 57) * (1 / 57))",
|
||||
exponentiateBySquaring(Expr("57"), -2).expr,
|
||||
"tried exponentiationBySquaring(57, -2)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * (57 * 57))",
|
||||
exponentiateBySquaring(Expr("57"), 3).expr,
|
||||
"tried exponentiationBySquaring(57, 3)"
|
||||
)
|
||||
assertEquals(
|
||||
"((1 / 57) * ((1 / 57) * (1 / 57)))",
|
||||
exponentiateBySquaring(Expr("57"), -3).expr,
|
||||
"tried exponentiationBySquaring(57, -3)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 57) * (57 * 57))",
|
||||
exponentiateBySquaring(Expr("57"), 4).expr,
|
||||
"tried exponentiationBySquaring(57, 4)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((1 / 57) * (1 / 57)) * ((1 / 57) * (1 / 57)))",
|
||||
exponentiateBySquaring(Expr("57"), -4).expr,
|
||||
"tried exponentiationBySquaring(57, -4)"
|
||||
)
|
||||
assertEquals(
|
||||
"(57 * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 5).expr,
|
||||
"tried exponentiationBySquaring(57, 5)"
|
||||
)
|
||||
assertEquals(
|
||||
"((1 / 57) * (((1 / 57) * (1 / 57)) * ((1 / 57) * (1 / 57))))",
|
||||
exponentiateBySquaring(Expr("57"), -5).expr,
|
||||
"tried exponentiationBySquaring(57, -5)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * 57) * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 6).expr,
|
||||
"tried exponentiationBySquaring(57, 6)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((1 / 57) * (1 / 57)) * (((1 / 57) * (1 / 57)) * ((1 / 57) * (1 / 57))))",
|
||||
exponentiateBySquaring(Expr("57"), -6).expr,
|
||||
"tried exponentiationBySquaring(57, -6)"
|
||||
)
|
||||
assertEquals(
|
||||
"((57 * (57 * 57)) * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 7).expr,
|
||||
"tried exponentiationBySquaring(57, 7)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((1 / 57) * ((1 / 57) * (1 / 57))) * (((1 / 57) * (1 / 57)) * ((1 / 57) * (1 / 57))))",
|
||||
exponentiateBySquaring(Expr("57"), -7).expr,
|
||||
"tried exponentiationBySquaring(57, -7)"
|
||||
)
|
||||
assertEquals(
|
||||
"(((57 * 57) * (57 * 57)) * ((57 * 57) * (57 * 57)))",
|
||||
exponentiateBySquaring(Expr("57"), 8).expr,
|
||||
"tried exponentiationBySquaring(57, 8)"
|
||||
)
|
||||
assertEquals(
|
||||
"((((1 / 57) * (1 / 57)) * ((1 / 57) * (1 / 57))) * (((1 / 57) * (1 / 57)) * ((1 / 57) * (1 / 57))))",
|
||||
exponentiateBySquaring(Expr("57"), -8).expr,
|
||||
"tried exponentiationBySquaring(57, -8)"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.functions.testUtils.t
|
||||
import space.kscience.kmath.functions.testUtils.x
|
||||
import space.kscience.kmath.functions.testUtils.y
|
||||
import space.kscience.kmath.functions.testUtils.z
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.algebra
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class LabeledConstructorsTest {
|
||||
@Test
|
||||
@UnstableKMathAPI
|
||||
fun testDSL1() {
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(x to 2u, z to 3u) to 5,
|
||||
mapOf(y to 1u) to -6,
|
||||
),
|
||||
Int.algebra.labeledPolynomialSpace {
|
||||
LabeledPolynomialDSL1 {
|
||||
5 { x pow 2u; z pow 3u }
|
||||
(-6) { y pow 1u }
|
||||
}
|
||||
},
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf<Symbol, UInt>() to -1,
|
||||
),
|
||||
Int.algebra.labeledPolynomialSpace {
|
||||
LabeledPolynomialDSL1 {
|
||||
5 { }
|
||||
(-6) { }
|
||||
}
|
||||
},
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(x to 2u) to -1,
|
||||
),
|
||||
Int.algebra.labeledPolynomialSpace {
|
||||
LabeledPolynomialDSL1 {
|
||||
5 { x pow 1u; x pow 1u }
|
||||
(-6) { x pow 2u }
|
||||
}
|
||||
},
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(x to 2u) to -1,
|
||||
),
|
||||
Int.algebra.labeledPolynomialSpace {
|
||||
LabeledPolynomialDSL1 {
|
||||
5 { x pow 1u; x pow 1u }
|
||||
(-6) { x pow 2u; z pow 0u }
|
||||
}
|
||||
},
|
||||
"test 3"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
@UnstableKMathAPI
|
||||
fun testFabric() {
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(x to 2u, z to 3u) to 5,
|
||||
mapOf(y to 1u) to -6,
|
||||
),
|
||||
Int.algebra {
|
||||
LabeledPolynomial(
|
||||
mapOf(x to 2u, z to 3u) to 5,
|
||||
mapOf(y to 1u) to -6,
|
||||
)
|
||||
},
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf(x to 2u, z to 3u) to 5,
|
||||
mapOf(y to 1u) to -6,
|
||||
),
|
||||
Int.algebra {
|
||||
LabeledPolynomial(
|
||||
mapOf(x to 2u, y to 0u, z to 3u, t to 0u) to 5,
|
||||
mapOf(x to 0u, y to 1u, z to 0u, t to 0u) to -6,
|
||||
)
|
||||
},
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf<Symbol, UInt>() to -1,
|
||||
),
|
||||
Int.algebra {
|
||||
LabeledPolynomial(
|
||||
mapOf(x to 0u) to 5,
|
||||
mapOf(y to 0u, z to 0u) to -6,
|
||||
)
|
||||
},
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
LabeledPolynomialAsIs(
|
||||
mapOf<Symbol, UInt>() to 0,
|
||||
),
|
||||
Int.algebra {
|
||||
LabeledPolynomial(
|
||||
mapOf(x to 0u) to 5,
|
||||
mapOf(z to 0u, t to 0u) to -5,
|
||||
)
|
||||
},
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,544 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("LocalVariableName")
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.functions.testUtils.IntModuloRing
|
||||
import space.kscience.kmath.functions.testUtils.ListPolynomial
|
||||
import space.kscience.kmath.functions.testUtils.Rational
|
||||
import space.kscience.kmath.functions.testUtils.RationalField
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
class ListPolynomialTest {
|
||||
@Test
|
||||
fun test_Polynomial_Int_plus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + -3,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + 2,
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial(Rational(-2)) + 2,
|
||||
"test 3"
|
||||
)
|
||||
val polynomial_4 = ListPolynomial<Rational>()
|
||||
assertSame(
|
||||
polynomial_4,
|
||||
polynomial_4 + 0,
|
||||
"test 4"
|
||||
)
|
||||
val polynomial_5 = ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7))
|
||||
assertSame(
|
||||
polynomial_5,
|
||||
polynomial_5 + 0,
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + 1,
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1)),
|
||||
ListPolynomial(Rational(-2)) + 1,
|
||||
"test 7"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(2)),
|
||||
ListPolynomial<Rational>() + 2,
|
||||
"test 8"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Int_minus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) - -3,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - 2,
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial(Rational(2)) - 2,
|
||||
"test 3"
|
||||
)
|
||||
val polynomial_4 = ListPolynomial<Rational>()
|
||||
assertSame(
|
||||
polynomial_4,
|
||||
polynomial_4 - 0,
|
||||
"test 4"
|
||||
)
|
||||
val polynomial_5 = ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7))
|
||||
assertEquals(
|
||||
polynomial_5,
|
||||
polynomial_5 - 0,
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - 1,
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1)),
|
||||
ListPolynomial(Rational(2)) - 1,
|
||||
"test 7"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2)),
|
||||
ListPolynomial<Rational>() - 2,
|
||||
"test 8"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Int_times() {
|
||||
IntModuloRing(35).listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(34, 2, 1, 20, 2),
|
||||
ListPolynomial(22, 26, 13, 15, 26) * 27,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(0, 0, 0, 0, 0),
|
||||
ListPolynomial(7, 0, 49, 21, 14) * 15,
|
||||
"test 2"
|
||||
)
|
||||
val polynomial = ListPolynomial(22, 26, 13, 15, 26)
|
||||
assertSame(
|
||||
zero,
|
||||
polynomial * 0,
|
||||
"test 3"
|
||||
)
|
||||
assertSame(
|
||||
polynomial,
|
||||
polynomial * 1,
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Int_Polynomial_plus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
-3 + ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
2 + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
2 + ListPolynomial(Rational(-2)),
|
||||
"test 3"
|
||||
)
|
||||
val polynomial_4 = ListPolynomial<Rational>()
|
||||
assertSame(
|
||||
polynomial_4,
|
||||
0 + polynomial_4,
|
||||
"test 4"
|
||||
)
|
||||
val polynomial_5 = ListPolynomial<Rational>(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7))
|
||||
assertSame(
|
||||
polynomial_5,
|
||||
0 + polynomial_5,
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
|
||||
1 + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1)),
|
||||
1 + ListPolynomial(Rational(-2)),
|
||||
"test 7"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(2)),
|
||||
2 + ListPolynomial(),
|
||||
"test 8"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Int_Polynomial_minus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
3 - ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
-2 - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
-2 - ListPolynomial(Rational(-2)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-32, 9), Rational(-8, -9), Rational(8, 7)),
|
||||
0 - ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(),
|
||||
0 - ListPolynomial(),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
|
||||
-1 - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1)),
|
||||
-1 - ListPolynomial(Rational(-2)),
|
||||
"test 7"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2)),
|
||||
-2 - ListPolynomial(),
|
||||
"test 8"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Int_Polynomial_times() {
|
||||
IntModuloRing(35).listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(34, 2, 1, 20, 2),
|
||||
27 * ListPolynomial(22, 26, 13, 15, 26),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(0, 0, 0, 0, 0),
|
||||
15 * ListPolynomial(7, 0, 49, 21, 14),
|
||||
"test 2"
|
||||
)
|
||||
val polynomial = ListPolynomial(22, 26, 13, 15, 26)
|
||||
assertSame(
|
||||
zero,
|
||||
0 * polynomial,
|
||||
"test 3"
|
||||
)
|
||||
assertSame(
|
||||
polynomial,
|
||||
1 * polynomial,
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Constant_plus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) + Rational(-3),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + Rational(2),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial(Rational(-2)) + Rational(2),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial<Rational>() + Rational(0),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)) + Rational(1),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1)),
|
||||
ListPolynomial(Rational(-2)) + Rational(1),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(2)),
|
||||
ListPolynomial<Rational>() + Rational(2),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Constant_minus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) - Rational(-3),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - Rational(2),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial(Rational(2)) - Rational(2),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial<Rational>() - Rational(0),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(2), Rational(0), Rational(0), Rational(0)) - Rational(1),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1)),
|
||||
ListPolynomial(Rational(2)) - Rational(1),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2)),
|
||||
ListPolynomial<Rational>() - Rational(2),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Constant_times() {
|
||||
IntModuloRing(35).listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(34, 2, 1, 20, 2),
|
||||
ListPolynomial(22, 26, 13, 15, 26) * 27.asConstant(),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(0, 0, 0, 0, 0),
|
||||
ListPolynomial(7, 0, 49, 21, 14) * 15.asConstant(),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Constant_Polynomial_plus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-22, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
Rational(-3) + ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(2) + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
Rational(2) + ListPolynomial(Rational(-2)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
Rational(0) + ListPolynomial(),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(1) + ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-1)),
|
||||
Rational(1) + ListPolynomial(Rational(-2)),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(2)),
|
||||
Rational(2) + ListPolynomial(),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Constant_Polynomial_minus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(32, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
Rational(3) - ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(-2) - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
Rational(-2) - ListPolynomial(Rational(-2)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
Rational(0) - ListPolynomial(),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1), Rational(0), Rational(0), Rational(0)),
|
||||
Rational(-1) - ListPolynomial(Rational(-2), Rational(0), Rational(0), Rational(0)),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1)),
|
||||
Rational(-1) - ListPolynomial(Rational(-2)),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2)),
|
||||
Rational(-2) - ListPolynomial(),
|
||||
"test 7"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Constant_Polynomial_times() {
|
||||
IntModuloRing(35).listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(34, 2, 1, 20, 2),
|
||||
27 * ListPolynomial(22, 26, 13, 15, 26),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(0, 0, 0, 0, 0),
|
||||
15 * ListPolynomial(7, 0, 49, 21, 14),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_unaryMinus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7)),
|
||||
-ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-5, 9), Rational(8, 9), Rational(8, 7), Rational(0), Rational(0)),
|
||||
-ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7), Rational(0), Rational(0)),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Polynomial_plus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
// (5/9 - 8/9 x - 8/7 x^2) + (-5/7 + 5/1 x + 5/8 x^2) ?= -10/63 + 37/9 x - 29/56 x^2
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-10, 63), Rational(37, 9), Rational(-29, 56)),
|
||||
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) +
|
||||
ListPolynomial(Rational(-5, 7), Rational(5, 1), Rational(5, 8)),
|
||||
"test 1"
|
||||
)
|
||||
// (-2/9 - 8/3 x) + (0 + 9/4 x + 2/4 x^2) ?= -2/9 - 5/12 x + 2/4 x^2
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2, 9), Rational(-5, 12), Rational(2, 4)),
|
||||
ListPolynomial(Rational(-2, 9), Rational(-8, 3)) +
|
||||
ListPolynomial(Rational(0), Rational(9, 4), Rational(2, 4)),
|
||||
"test 2"
|
||||
)
|
||||
// (-4/7 - 2/6 x + 0 x^2 + 0 x^3) + (-6/3 - 7/2 x + 2/3 x^2) ?= -18/7 - 23/6 x + 2/3 x^2
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-18, 7), Rational(-23, 6), Rational(2, 3), Rational(0)),
|
||||
ListPolynomial(Rational(-4, 7), Rational(-2, 6), Rational(0), Rational(0)) +
|
||||
ListPolynomial(Rational(-6, 3), Rational(-7, 2), Rational(2, 3)),
|
||||
"test 3"
|
||||
)
|
||||
// (-2/4 - 6/9 x - 4/9 x^2) + (2/4 + 6/9 x + 4/9 x^2) ?= 0
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)) +
|
||||
ListPolynomial(Rational(2, 4), Rational(6, 9), Rational(4, 9)),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Polynomial_minus() {
|
||||
RationalField.listPolynomialSpace {
|
||||
// (5/9 - 8/9 x - 8/7 x^2) - (-5/7 + 5/1 x + 5/8 x^2) ?= 80/63 - 53/9 x - 99/56 x^2
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(80, 63), Rational(-53, 9), Rational(-99, 56)),
|
||||
ListPolynomial(Rational(5, 9), Rational(-8, 9), Rational(-8, 7)) -
|
||||
ListPolynomial(Rational(-5, 7), Rational(5, 1), Rational(5, 8)),
|
||||
"test 1"
|
||||
)
|
||||
// (-2/9 - 8/3 x) - (0 + 9/4 x + 2/4 x^2) ?= -2/9 - 59/12 x - 2/4 x^2
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2, 9), Rational(-59, 12), Rational(-2, 4)),
|
||||
ListPolynomial(Rational(-2, 9), Rational(-8, 3)) -
|
||||
ListPolynomial(Rational(0), Rational(9, 4), Rational(2, 4)),
|
||||
"test 2"
|
||||
)
|
||||
// (-4/7 - 2/6 x + 0 x^2 + 0 x^3) - (-6/3 - 7/2 x + 2/3 x^2) ?= 10/7 + 19/6 x - 2/3 x^2
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(10, 7), Rational(19, 6), Rational(-2, 3), Rational(0)),
|
||||
ListPolynomial(Rational(-4, 7), Rational(-2, 6), Rational(0), Rational(0)) -
|
||||
ListPolynomial(Rational(-6, 3), Rational(-7, 2), Rational(2, 3)),
|
||||
"test 3"
|
||||
)
|
||||
// (-2/4 - 6/9 x - 4/9 x^2) - (-2/4 - 6/9 x - 4/9 x^2) ?= 0
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)) -
|
||||
ListPolynomial(Rational(-2, 4), Rational(-6, 9), Rational(-4, 9)),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_Polynomial_times() {
|
||||
IntModuloRing(35).listPolynomialSpace {
|
||||
// (1 + x + x^2) * (1 - x + x^2) ?= 1 + x^2 + x^4
|
||||
assertEquals(
|
||||
ListPolynomial(1, 0, 1, 0, 1),
|
||||
ListPolynomial(1, -1, 1) * ListPolynomial(1, 1, 1),
|
||||
"test 1"
|
||||
)
|
||||
// Spoiler: 5 * 7 = 0
|
||||
assertEquals(
|
||||
ListPolynomial(0, 0, 0, 0, 0),
|
||||
ListPolynomial(5, -25, 10) * ListPolynomial(21, 14, -7),
|
||||
"test 2"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,982 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.functions.testUtils.Rational
|
||||
import space.kscience.kmath.functions.testUtils.RationalField
|
||||
import space.kscience.kmath.functions.testUtils.assertFailsWithTypeAndMessage
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
class ListPolynomialUtilTest {
|
||||
@Test
|
||||
fun test_Polynomial_substitute_Double() {
|
||||
assertEquals(
|
||||
0.0,
|
||||
ListPolynomial(1.0, -2.0, 1.0).substitute(1.0),
|
||||
0.001,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
0.0,
|
||||
ListPolynomial(1.0, -2.0, 1.0).substitute(1.0),
|
||||
0.001,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
1.1931904761904761,
|
||||
ListPolynomial(0.625, 2.6666666666666665, 0.5714285714285714, 1.5).substitute(0.2),
|
||||
0.001,
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
0.5681904761904762,
|
||||
ListPolynomial(0.0, 2.6666666666666665, 0.5714285714285714, 1.5).substitute(0.2),
|
||||
0.001,
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
1.1811904761904761,
|
||||
ListPolynomial(0.625, 2.6666666666666665, 0.5714285714285714, 0.0).substitute(0.2),
|
||||
0.001,
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
1.1703333333333332,
|
||||
ListPolynomial(0.625, 2.6666666666666665, 0.0, 1.5).substitute(0.2),
|
||||
0.001,
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_substitute_Constant() {
|
||||
assertEquals(
|
||||
Rational(0),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).substitute(RationalField, Rational(1)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(25057, 21000),
|
||||
ListPolynomial(Rational(5, 8), Rational(8, 3), Rational(4, 7), Rational(3, 2))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(2983, 5250),
|
||||
ListPolynomial(Rational(0), Rational(8, 3), Rational(4, 7), Rational(3, 2))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(4961, 4200),
|
||||
ListPolynomial(Rational(5, 8), Rational(8, 3), Rational(4, 7), Rational(0))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(3511, 3000),
|
||||
ListPolynomial(Rational(5, 8), Rational(8, 3), Rational(0), Rational(3, 2))
|
||||
.substitute(RationalField, Rational(1, 5)),
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_substitute_Polynomial() {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).substitute(RationalField, ListPolynomial(Rational(1))),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(709, 378), Rational(155, 252), Rational(19, 525), Rational(2, 875)),
|
||||
ListPolynomial(Rational(1, 7), Rational(9, 4), Rational(1, 3), Rational(2, 7))
|
||||
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(655, 378), Rational(155, 252), Rational(19, 525), Rational(2, 875)),
|
||||
ListPolynomial(Rational(0), Rational(9, 4), Rational(1, 3), Rational(2, 7))
|
||||
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(677, 378), Rational(97, 180), Rational(1, 75), Rational(0)),
|
||||
ListPolynomial(Rational(1, 7), Rational(9, 4), Rational(1, 3), Rational(0))
|
||||
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(653, 378), Rational(221, 420), Rational(4, 175), Rational(2, 875)),
|
||||
ListPolynomial(Rational(1, 7), Rational(9, 4), Rational(0), Rational(2, 7))
|
||||
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(1, 5))),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(89, 54), Rational(0), Rational(0), Rational(0)),
|
||||
ListPolynomial(Rational(0), Rational(9, 4), Rational(1, 3), Rational(0))
|
||||
.substitute(RationalField, ListPolynomial(Rational(6, 9), Rational(0))),
|
||||
"test 6"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
@Ignore // FIXME: This tests work only for sane realisations of the substitutions. Currently, it is not.
|
||||
// Sane algorithm for substitution p(q/r) (p, q, and r are polynomials) should return denominator r^deg(p),
|
||||
// not r^(deg(p)(deg(p)+1)/2) as it is now.
|
||||
fun test_Polynomial_substitute_RationalFunction() {
|
||||
assertEquals(
|
||||
ListRationalFunction(ListPolynomial(Rational(0)), ListPolynomial(Rational(1))),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1))
|
||||
.substitute(RationalField, ListRationalFunction(ListPolynomial(Rational(1)), ListPolynomial(Rational(1)))),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(66349, 243),
|
||||
Rational(-17873, 405),
|
||||
Rational(173533, 3780),
|
||||
Rational(-91141, 567),
|
||||
Rational(5773909, 105840),
|
||||
Rational(-23243, 630),
|
||||
Rational(1573, 27)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(169, 81),
|
||||
Rational(-130, 27),
|
||||
Rational(115, 18),
|
||||
Rational(-797, 54),
|
||||
Rational(1985, 144),
|
||||
Rational(-55, 6),
|
||||
Rational(121, 9)
|
||||
)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(13, 3),
|
||||
Rational(-9, 5),
|
||||
Rational(5, 5)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(15, 1),
|
||||
Rational(6, 9),
|
||||
Rational(-3, 7)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-13, 9),
|
||||
Rational(10, 6),
|
||||
Rational(-10, 8),
|
||||
Rational(11, 3)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(-14, 9),
|
||||
Rational(31, 14),
|
||||
Rational(-5077, 980),
|
||||
Rational(99, 35)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(25, 9),
|
||||
Rational(-25, 6),
|
||||
Rational(1985, 144),
|
||||
Rational(-55, 6),
|
||||
Rational(121, 9)
|
||||
)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0),
|
||||
Rational(-9, 5),
|
||||
Rational(5, 5)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(0),
|
||||
Rational(6, 9),
|
||||
Rational(-3, 7)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0),
|
||||
Rational(10, 6),
|
||||
Rational(-10, 8),
|
||||
Rational(11, 3)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(-898, 27),
|
||||
Rational(271, 45),
|
||||
Rational(-65, 12) ,
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-13, 9),
|
||||
Rational(5, 3),
|
||||
Rational(-5, 4),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0)
|
||||
)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(13, 3),
|
||||
Rational(-9, 5),
|
||||
Rational(0)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(15, 1),
|
||||
Rational(6, 9),
|
||||
Rational(0)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-13, 9),
|
||||
Rational(10, 6),
|
||||
Rational(-10, 8),
|
||||
Rational(0)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(56872, 243),
|
||||
Rational(0, 1),
|
||||
Rational(-90, 7),
|
||||
Rational(-3718, 81),
|
||||
Rational(9, 49),
|
||||
Rational(0, 1),
|
||||
Rational(1573, 27)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(169, 81),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(-286, 27),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(121, 9)
|
||||
)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(13, 3),
|
||||
Rational(0),
|
||||
Rational(5, 5)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(15, 1),
|
||||
Rational(0),
|
||||
Rational(-3, 7)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-13, 9),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(11, 3)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_RationalFunction_substitute_Double() {
|
||||
assertEquals(
|
||||
0.0,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(1.0, -2.0, 1.0),
|
||||
ListPolynomial(-6.302012278484357, 5.831971885376948, -9.271604788393432, 5.494387848015814, -3.7187384450880785)
|
||||
).substitute(1.0),
|
||||
0.001,
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
2.693702616649797,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(-5.848840571263625, -1.660411278951134, -3.793740946372443, -9.624569269490076),
|
||||
ListPolynomial(-2.9680680215311073, -1.862973627119981, 4.776550592888336, -2.7320154512368466)
|
||||
).substitute(-7.53452770353279),
|
||||
0.001,
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
2.692226268901378,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(0.0, -1.660411278951134, -3.793740946372443, -9.624569269490076),
|
||||
ListPolynomial(0.0, -1.862973627119981, 4.776550592888336, -2.7320154512368466)
|
||||
).substitute(-7.53452770353279),
|
||||
0.001,
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
-0.7394904842099175,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(-5.848840571263625, -1.660411278951134, -3.793740946372443, 0.0),
|
||||
ListPolynomial(-2.9680680215311073, -1.862973627119981, 4.776550592888336, 0.0)
|
||||
).substitute(-7.53452770353279),
|
||||
0.001,
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
3.526835209398159,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(-5.848840571263625, 0.0, 0.0, -9.624569269490076),
|
||||
ListPolynomial(-2.9680680215311073, 0.0, 0.0, -2.7320154512368466)
|
||||
).substitute(-7.53452770353279),
|
||||
0.001,
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_RationalFunction_substitute_Constant() {
|
||||
assertEquals(
|
||||
Rational(0),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)),
|
||||
ListPolynomial(Rational(1)),
|
||||
).substitute(RationalField, Rational(1)),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(1149615, 61306),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(17, 7), Rational(18, 3), Rational(18, 8), Rational(9, 1)),
|
||||
ListPolynomial(Rational(11, 9), Rational(-6, 5), Rational(-12, 7), Rational(2, 1)),
|
||||
).substitute(RationalField, Rational(-7, 8)),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(3495, 586),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(0), Rational(18, 3), Rational(18, 8), Rational(9, 1)),
|
||||
ListPolynomial(Rational(0), Rational(-6, 5), Rational(-12, 7), Rational(2, 1)),
|
||||
).substitute(RationalField, Rational(-7, 8)),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(-88605, 77392),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(17, 7), Rational(18, 3), Rational(18, 8), Rational(0)),
|
||||
ListPolynomial(Rational(11, 9), Rational(-6, 5), Rational(-12, 7), Rational(0)),
|
||||
).substitute(RationalField, Rational(-7, 8)),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
Rational(116145, 3794),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(17, 7), Rational(0), Rational(0), Rational(9, 1)),
|
||||
ListPolynomial(Rational(11, 9), Rational(0), Rational(0), Rational(2, 1)),
|
||||
).substitute(RationalField, Rational(-7, 8)),
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_RationalFunction_substitute_Polynomial() {
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial(Rational(1))
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)),
|
||||
ListPolynomial(Rational(1)),
|
||||
).substitute(RationalField, ListPolynomial(Rational(1))),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(-283303, 36),
|
||||
Rational(-23593, 24),
|
||||
Rational(368713, 192),
|
||||
Rational(1455, 8),
|
||||
Rational(-272171, 1536),
|
||||
Rational(-2149, 192),
|
||||
Rational(469, 64),
|
||||
Rational(11, 48),
|
||||
Rational(-11, 96)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(5797, 12),
|
||||
Rational(595, 16),
|
||||
Rational(-5285, 72),
|
||||
Rational(-745, 192),
|
||||
Rational(1105, 288),
|
||||
Rational(5, 48),
|
||||
Rational(-5, 72)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(2, 9),
|
||||
Rational(11, 3),
|
||||
Rational(-9, 4),
|
||||
Rational(-6, 1),
|
||||
Rational(-11, 6)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-2, 3),
|
||||
Rational(-15, 4),
|
||||
Rational(5, 9),
|
||||
Rational(-5, 9)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListPolynomial(
|
||||
Rational(-9, 1),
|
||||
Rational(-1, 4),
|
||||
Rational(2, 4)
|
||||
)
|
||||
),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(0, 1),
|
||||
Rational(-11, 12),
|
||||
Rational(325, 192),
|
||||
Rational(21, 32),
|
||||
Rational(-1739, 1536),
|
||||
Rational(227, 192),
|
||||
Rational(-59, 64),
|
||||
Rational(11, 48),
|
||||
Rational(-11, 96)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0, 1),
|
||||
Rational(15, 16),
|
||||
Rational(-265, 144),
|
||||
Rational(-25, 192),
|
||||
Rational(25, 288),
|
||||
Rational(5, 48),
|
||||
Rational(-5, 72)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(0, 9),
|
||||
Rational(11, 3),
|
||||
Rational(-9, 4),
|
||||
Rational(-6, 1),
|
||||
Rational(-11, 6)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0, 3),
|
||||
Rational(-15, 4),
|
||||
Rational(5, 9),
|
||||
Rational(-5, 9)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListPolynomial(
|
||||
Rational(0, 1),
|
||||
Rational(-1, 4),
|
||||
Rational(2, 4)
|
||||
)
|
||||
),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(149723, 36),
|
||||
Rational(8483, 24),
|
||||
Rational(639, 64),
|
||||
Rational(3, 32),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(937, 12),
|
||||
Rational(55, 16),
|
||||
Rational(5, 144),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(2, 9),
|
||||
Rational(11, 3),
|
||||
Rational(-9, 4),
|
||||
Rational(-6, 1),
|
||||
Rational(0)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-2, 3),
|
||||
Rational(-15, 4),
|
||||
Rational(5, 9),
|
||||
Rational(0)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListPolynomial(
|
||||
Rational(-9, 1),
|
||||
Rational(-1, 4),
|
||||
Rational(0)
|
||||
)
|
||||
),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(-216509, 18),
|
||||
Rational(0, 1),
|
||||
Rational(2673, 1),
|
||||
Rational(0, 1),
|
||||
Rational(-891, 4),
|
||||
Rational(0, 1),
|
||||
Rational(33, 4),
|
||||
Rational(0, 1),
|
||||
Rational(-11, 96)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(1213, 3),
|
||||
Rational(0, 1),
|
||||
Rational(-135, 2),
|
||||
Rational(0, 1),
|
||||
Rational(15, 4),
|
||||
Rational(0, 1),
|
||||
Rational(-5, 72)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(2, 9),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(-11, 6)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-2, 3),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(-5, 9)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListPolynomial(
|
||||
Rational(-9, 1),
|
||||
Rational(0),
|
||||
Rational(2, 4)
|
||||
)
|
||||
),
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
@Ignore // FIXME: This tests work only for sane realisations of the substitutions. Currently, it is not.
|
||||
// Sane algorithm for substitution p(q/r) (p, q, and r are polynomials) should return denominator r^deg(p),
|
||||
// not r^(deg(p)(deg(p)+1)/2) as it is now.
|
||||
fun test_RationalFunction_substitute_RationalFunction() {
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(0)),
|
||||
ListPolynomial(Rational(1))
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)),
|
||||
ListPolynomial(Rational(1))
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(Rational(1)),
|
||||
ListPolynomial(Rational(1))
|
||||
)
|
||||
),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(130087, 3888),
|
||||
Rational(-2866333, 65610),
|
||||
Rational(-5076229, 97200),
|
||||
Rational(222136997, 3280500),
|
||||
Rational(754719329, 20995200),
|
||||
Rational(-12010283, 324000),
|
||||
Rational(-2011967, 172800),
|
||||
Rational(18607, 2880),
|
||||
Rational(4705, 4096)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-143820355, 3779136),
|
||||
Rational(73886869, 1574640),
|
||||
Rational(1440175193, 15746400),
|
||||
Rational(-5308968857, 52488000),
|
||||
Rational(-186910083731, 2099520000),
|
||||
Rational(125043463, 1555200),
|
||||
Rational(5299123, 388800),
|
||||
Rational(-213757, 15360),
|
||||
Rational(1380785, 147456)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(1, 1),
|
||||
Rational(-10, 5),
|
||||
Rational(18, 8),
|
||||
Rational(-8, 8)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-14, 8),
|
||||
Rational(-14, 8),
|
||||
Rational(-19, 6),
|
||||
Rational(14, 3),
|
||||
Rational(8, 9)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(14, 9),
|
||||
Rational(-2, 5),
|
||||
Rational(-14, 7)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-6, 4),
|
||||
Rational(5, 9),
|
||||
Rational(1, 8)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(5173, 18225),
|
||||
Rational(904291, 364500),
|
||||
Rational(283127, 43200),
|
||||
Rational(37189, 5760),
|
||||
Rational(147, 128)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(-163589, 911250),
|
||||
Rational(-881831, 291600),
|
||||
Rational(-10722229, 777600),
|
||||
Rational(-640921, 46080),
|
||||
Rational(86303, 9216)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(0),
|
||||
Rational(-10, 5),
|
||||
Rational(18, 8),
|
||||
Rational(-8, 8)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0),
|
||||
Rational(-14, 8),
|
||||
Rational(-19, 6),
|
||||
Rational(14, 3),
|
||||
Rational(8, 9)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(0),
|
||||
Rational(-2, 5),
|
||||
Rational(-14, 7)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(0),
|
||||
Rational(5, 9),
|
||||
Rational(1, 8)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(445, 16),
|
||||
Rational(-2011, 54),
|
||||
Rational(1359199, 72900),
|
||||
Rational(-135733, 32805),
|
||||
Rational(2254, 6561),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-2018387, 46656),
|
||||
Rational(82316437, 1574640),
|
||||
Rational(-9335047, 393660),
|
||||
Rational(15765889, 3280500),
|
||||
Rational(-242089, 656100),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1),
|
||||
Rational(0, 1)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(1, 1),
|
||||
Rational(-10, 5),
|
||||
Rational(18, 8),
|
||||
Rational(0)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-14, 8),
|
||||
Rational(-14, 8),
|
||||
Rational(-19, 6),
|
||||
Rational(14, 3),
|
||||
Rational(0)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(14, 9),
|
||||
Rational(-2, 5),
|
||||
Rational(0)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-6, 4),
|
||||
Rational(5, 9),
|
||||
Rational(0)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(41635, 3888),
|
||||
Rational(0, 1),
|
||||
Rational(-279187, 11664),
|
||||
Rational(0, 1),
|
||||
Rational(103769, 3456),
|
||||
Rational(0, 1),
|
||||
Rational(-11017, 768),
|
||||
Rational(0, 1),
|
||||
Rational(4097, 4096)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-13811791, 3779136),
|
||||
Rational(0, 1),
|
||||
Rational(-9999395, 419904),
|
||||
Rational(0, 1),
|
||||
Rational(6376601, 124416),
|
||||
Rational(0, 1),
|
||||
Rational(-3668315, 82944),
|
||||
Rational(0, 1),
|
||||
Rational(2097089, 147456)
|
||||
)
|
||||
),
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(1, 1),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(-8, 8)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-14, 8),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(0),
|
||||
Rational(8, 9)
|
||||
)
|
||||
).substitute(RationalField,
|
||||
ListRationalFunction(
|
||||
ListPolynomial(
|
||||
Rational(14, 9),
|
||||
Rational(0),
|
||||
Rational(-14, 7)
|
||||
),
|
||||
ListPolynomial(
|
||||
Rational(-6, 4),
|
||||
Rational(0),
|
||||
Rational(1, 8)
|
||||
)
|
||||
)
|
||||
),
|
||||
"test 5"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_derivative() {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2), Rational(2)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).derivative(RationalField),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).derivative(RationalField),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(8, 9), Rational(15, 7), Rational(-20, 9)),
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).derivative(RationalField),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-8, 3), Rational(8, 9), Rational(15, 7), Rational(0)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).derivative(RationalField),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_nthDerivative() {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(-2), Rational(2)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 1),
|
||||
"test 1"
|
||||
)
|
||||
assertFailsWithTypeAndMessage<IllegalArgumentException>(
|
||||
"Order of derivative must be non-negative",
|
||||
"test2"
|
||||
) {
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, -1)
|
||||
}
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 0),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(2)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 2),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 3),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthDerivative(RationalField, 4),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(8, 9), Rational(30, 7), Rational(-20, 3)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthDerivative(RationalField, 2),
|
||||
"test 7"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(8, 9), Rational(30, 7), Rational(-20, 3)),
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthDerivative(RationalField, 2),
|
||||
"test 8"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(8, 9), Rational(30, 7), Rational(0)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).nthDerivative(RationalField, 2),
|
||||
"test 9"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_antiderivative() {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).antiderivative(RationalField),
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).antiderivative(RationalField),
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(4, 27), Rational(5, 28), Rational(-1, 9)),
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).antiderivative(RationalField),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(1, 5), Rational(-4, 3), Rational(4, 27), Rational(5, 28), Rational(0)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).antiderivative(RationalField),
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
fun test_Polynomial_nthAntiderivative() {
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(1), Rational(-1), Rational(1, 3)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 1),
|
||||
"test 1"
|
||||
)
|
||||
assertFailsWithTypeAndMessage<IllegalArgumentException>(
|
||||
"Order of antiderivative must be non-negative",
|
||||
"test2"
|
||||
) {
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, -1)
|
||||
}
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 0),
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(1, 2), Rational(-1, 3), Rational(1, 12)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 2),
|
||||
"test 4"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(1, 6), Rational(-1, 12), Rational(1, 60)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 3),
|
||||
"test 5"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0), Rational(1, 24), Rational(-1, 60), Rational(1, 360)),
|
||||
ListPolynomial(Rational(1), Rational(-2), Rational(1)).nthAntiderivative(RationalField, 4),
|
||||
"test 6"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(1, 10), Rational(-4, 9), Rational(1, 27), Rational(1, 28), Rational(-1, 54)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthAntiderivative(RationalField, 2),
|
||||
"test 7"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(0), Rational(0), Rational(1, 27), Rational(1, 28), Rational(-1, 54)),
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(4, 9), Rational(5, 7), Rational(-5, 9)).nthAntiderivative(RationalField, 2),
|
||||
"test 8"
|
||||
)
|
||||
assertEquals(
|
||||
ListPolynomial(Rational(0), Rational(0), Rational(1, 10), Rational(-4, 9), Rational(1, 27), Rational(1, 28), Rational(0)),
|
||||
ListPolynomial(Rational(1, 5), Rational(-8, 3), Rational(4, 9), Rational(5, 7), Rational(0)).nthAntiderivative(RationalField, 2),
|
||||
"test 9"
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.algebra
|
||||
import space.kscience.kmath.operations.invoke
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
|
||||
class NumberedConstructorsTest {
|
||||
@Test
|
||||
@UnstableKMathAPI
|
||||
fun testDSL1() {
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf(2u, 0u, 3u) to 5,
|
||||
listOf(0u, 1u) to -6,
|
||||
),
|
||||
Int.algebra.numberedPolynomialSpace {
|
||||
NumberedPolynomialDSL1 {
|
||||
5 { 0 pow 2u; 2 pow 3u }
|
||||
(-6) { 1 pow 1u }
|
||||
}
|
||||
},
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf<UInt>() to -1,
|
||||
),
|
||||
Int.algebra.numberedPolynomialSpace {
|
||||
NumberedPolynomialDSL1 {
|
||||
5 { }
|
||||
(-6) { }
|
||||
}
|
||||
},
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf(2u) to -1,
|
||||
),
|
||||
Int.algebra.numberedPolynomialSpace {
|
||||
NumberedPolynomialDSL1 {
|
||||
5 { 0 pow 1u; 0 pow 1u }
|
||||
(-6) { 0 pow 2u }
|
||||
}
|
||||
},
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf(2u) to -1,
|
||||
),
|
||||
Int.algebra.numberedPolynomialSpace {
|
||||
NumberedPolynomialDSL1 {
|
||||
5 { 0 pow 1u; 0 pow 1u }
|
||||
(-6) { 0 pow 2u; 2 pow 0u }
|
||||
}
|
||||
},
|
||||
"test 3"
|
||||
)
|
||||
}
|
||||
@Test
|
||||
@UnstableKMathAPI
|
||||
fun testFabric() {
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf(2u, 0u, 3u) to 5,
|
||||
listOf(0u, 1u) to -6,
|
||||
),
|
||||
Int.algebra {
|
||||
NumberedPolynomial(
|
||||
listOf(2u, 0u, 3u) to 5,
|
||||
listOf(0u, 1u) to -6,
|
||||
)
|
||||
},
|
||||
"test 1"
|
||||
)
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf(2u, 0u, 3u) to 5,
|
||||
listOf(0u, 1u) to -6,
|
||||
),
|
||||
Int.algebra {
|
||||
NumberedPolynomial(
|
||||
listOf(2u, 0u, 3u, 0u) to 5,
|
||||
listOf(0u, 1u, 0u, 0u) to -6,
|
||||
)
|
||||
},
|
||||
"test 2"
|
||||
)
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf<UInt>() to -1,
|
||||
),
|
||||
Int.algebra {
|
||||
NumberedPolynomial(
|
||||
listOf(0u) to 5,
|
||||
listOf(0u, 0u) to -6,
|
||||
)
|
||||
},
|
||||
"test 3"
|
||||
)
|
||||
assertEquals(
|
||||
NumberedPolynomialAsIs(
|
||||
listOf<UInt>() to 0,
|
||||
),
|
||||
Int.algebra {
|
||||
NumberedPolynomial(
|
||||
listOf(0u) to 5,
|
||||
listOf(0u, 0u) to -5,
|
||||
)
|
||||
},
|
||||
"test 4"
|
||||
)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.structures.Buffer
|
||||
import space.kscience.kmath.structures.asBuffer
|
||||
|
||||
|
||||
fun <T> bufferOf(vararg elements: T): Buffer<T> = elements.asBuffer()
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.operations.Ring
|
||||
|
||||
|
||||
class IntModulo {
|
||||
val residue: Int
|
||||
val modulus: Int
|
||||
|
||||
@PublishedApi
|
||||
internal constructor(residue: Int, modulus: Int, toCheckInput: Boolean = true) {
|
||||
if (toCheckInput) {
|
||||
require(modulus != 0) { "modulus can not be zero" }
|
||||
this.modulus = if (modulus < 0) -modulus else modulus
|
||||
this.residue = residue.mod(this.modulus)
|
||||
} else {
|
||||
this.residue = residue
|
||||
this.modulus = modulus
|
||||
}
|
||||
}
|
||||
|
||||
constructor(residue: Int, modulus: Int) : this(residue, modulus, true)
|
||||
|
||||
operator fun unaryPlus(): IntModulo = this
|
||||
operator fun unaryMinus(): IntModulo =
|
||||
IntModulo(
|
||||
if (residue == 0) 0 else modulus - residue,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun plus(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not add two residue different modulo" }
|
||||
return IntModulo(
|
||||
(residue + other.residue) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun plus(other: Int): IntModulo =
|
||||
IntModulo(
|
||||
(residue + other) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun minus(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not subtract two residue different modulo" }
|
||||
return IntModulo(
|
||||
(residue - other.residue) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun minus(other: Int): IntModulo =
|
||||
IntModulo(
|
||||
(residue - other) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun times(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not multiply two residue different modulo" }
|
||||
return IntModulo(
|
||||
(residue * other.residue) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun times(other: Int): IntModulo =
|
||||
IntModulo(
|
||||
(residue * other) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun div(other: IntModulo): IntModulo {
|
||||
require(modulus == other.modulus) { "can not divide two residue different modulo" }
|
||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other.residue, modulus)
|
||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
||||
return IntModulo(
|
||||
(residue * reciprocalCandidate) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun div(other: Int): IntModulo {
|
||||
val (reciprocalCandidate, gcdOfOtherResidueAndModulus) = bezoutIdentityWithGCD(other, modulus)
|
||||
require(gcdOfOtherResidueAndModulus == 1) { "can not divide to residue that has non-trivial GCD with modulo" }
|
||||
return IntModulo(
|
||||
(residue * reciprocalCandidate) % modulus,
|
||||
modulus,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean =
|
||||
when (other) {
|
||||
is IntModulo -> residue == other.residue && modulus == other.modulus
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = residue.hashCode()
|
||||
|
||||
override fun toString(): String = "$residue mod $modulus"
|
||||
}
|
||||
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE")
|
||||
class IntModuloRing : Ring<IntModulo> {
|
||||
|
||||
val modulus: Int
|
||||
|
||||
constructor(modulus: Int) {
|
||||
require(modulus != 0) { "modulus can not be zero" }
|
||||
this.modulus = if (modulus < 0) -modulus else modulus
|
||||
}
|
||||
|
||||
override inline val zero: IntModulo get() = IntModulo(0, modulus, toCheckInput = false)
|
||||
override inline val one: IntModulo get() = IntModulo(1, modulus, toCheckInput = false)
|
||||
|
||||
fun number(arg: Int): IntModulo = IntModulo(arg, modulus, toCheckInput = false)
|
||||
|
||||
override inline fun add(left: IntModulo, right: IntModulo): IntModulo = left + right
|
||||
override inline fun multiply(left: IntModulo, right: IntModulo): IntModulo = left * right
|
||||
|
||||
override inline fun IntModulo.unaryMinus(): IntModulo = -this
|
||||
override inline fun IntModulo.plus(arg: IntModulo): IntModulo = this + arg
|
||||
override inline fun IntModulo.minus(arg: IntModulo): IntModulo = this - arg
|
||||
override inline fun IntModulo.times(arg: IntModulo): IntModulo = this * arg
|
||||
inline fun IntModulo.div(arg: IntModulo): IntModulo = this / arg
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.functions.ListPolynomial
|
||||
import space.kscience.kmath.functions.ListPolynomialSpace
|
||||
import space.kscience.kmath.functions.PolynomialSpaceOverRing
|
||||
|
||||
|
||||
fun ListPolynomialSpace<IntModulo, IntModuloRing>.ListPolynomial(vararg coefs: Int): ListPolynomial<IntModulo> =
|
||||
ListPolynomial(coefs.map { IntModulo(it, ring.modulus) })
|
||||
fun IntModuloRing.ListPolynomial(vararg coefs: Int): ListPolynomial<IntModulo> =
|
||||
ListPolynomial(coefs.map { IntModulo(it, modulus) })
|
||||
|
||||
fun IntModuloRing.m(arg: Int): IntModulo = IntModulo(arg, modulus)
|
||||
fun PolynomialSpaceOverRing<IntModulo, *, IntModuloRing>.m(arg: Int): IntModulo = IntModulo(arg, ring.modulus)
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
internal data class BezoutIdentityWithGCD<T>(val first: T, val second: T, val gcd: T)
|
||||
|
||||
internal tailrec fun gcd(a: Long, b: Long): Long = if (a == 0L) abs(b) else gcd(b % a, a)
|
||||
|
||||
internal fun bezoutIdentityWithGCD(a: Int, b: Int): BezoutIdentityWithGCD<Int> =
|
||||
when {
|
||||
a < 0 && b < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, -second, gcd) }
|
||||
a < 0 -> with(bezoutIdentityWithGCDInternalLogic(-a, b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(-first, second, gcd) }
|
||||
b < 0 -> with(bezoutIdentityWithGCDInternalLogic(a, -b, 1, 0, 0, 1)) { BezoutIdentityWithGCD(first, -second, gcd) }
|
||||
else -> bezoutIdentityWithGCDInternalLogic(a, b, 1, 0, 0, 1)
|
||||
}
|
||||
|
||||
internal tailrec fun bezoutIdentityWithGCDInternalLogic(a: Int, b: Int, m1: Int, m2: Int, m3: Int, m4: Int): BezoutIdentityWithGCD<Int> =
|
||||
if (b == 0) BezoutIdentityWithGCD(m1, m3, a)
|
||||
else {
|
||||
val quotient = a / b
|
||||
val reminder = a % b
|
||||
bezoutIdentityWithGCDInternalLogic(b, reminder, m2, m1 - quotient * m2, m4, m3 - quotient * m4)
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||
import space.kscience.kmath.operations.Field
|
||||
import space.kscience.kmath.operations.NumbersAddOps
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
class Rational {
|
||||
companion object {
|
||||
val ZERO: Rational = Rational(0L)
|
||||
val ONE: Rational = Rational(1L)
|
||||
}
|
||||
|
||||
val numerator: Long
|
||||
val denominator: Long
|
||||
|
||||
internal constructor(numerator: Long, denominator: Long, toCheckInput: Boolean = true) {
|
||||
if (toCheckInput) {
|
||||
if (denominator == 0L) throw ArithmeticException("/ by zero")
|
||||
|
||||
val greatestCommonDivider = gcd(numerator, denominator).let { if (denominator < 0L) -it else it }
|
||||
|
||||
this.numerator = numerator / greatestCommonDivider
|
||||
this.denominator = denominator / greatestCommonDivider
|
||||
} else {
|
||||
this.numerator = numerator
|
||||
this.denominator = denominator
|
||||
}
|
||||
}
|
||||
|
||||
constructor(numerator: Int, denominator: Int) : this(numerator.toLong(), denominator.toLong(), true)
|
||||
constructor(numerator: Int, denominator: Long) : this(numerator.toLong(), denominator, true)
|
||||
constructor(numerator: Long, denominator: Int) : this(numerator, denominator.toLong(), true)
|
||||
constructor(numerator: Long, denominator: Long) : this(numerator, denominator, true)
|
||||
constructor(numerator: Int) : this(numerator.toLong(), 1L, false)
|
||||
constructor(numerator: Long) : this(numerator, 1L, false)
|
||||
|
||||
operator fun unaryPlus(): Rational = this
|
||||
operator fun unaryMinus(): Rational = Rational(-this.numerator, this.denominator)
|
||||
operator fun plus(other: Rational): Rational {
|
||||
val denominatorsGcd = gcd(denominator, other.denominator)
|
||||
val dividedThisDenominator = denominator / denominatorsGcd
|
||||
val dividedOtherDenominator = other.denominator / denominatorsGcd
|
||||
val numeratorCandidate = numerator * dividedOtherDenominator + dividedThisDenominator * other.numerator
|
||||
val secondGcd = gcd(numeratorCandidate, denominatorsGcd)
|
||||
return Rational(
|
||||
numeratorCandidate / secondGcd,
|
||||
dividedThisDenominator * (other.denominator / secondGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun plus(other: Int): Rational =
|
||||
Rational(
|
||||
numerator + denominator * other.toLong(),
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun plus(other: Long): Rational =
|
||||
Rational(
|
||||
numerator + denominator * other,
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun minus(other: Rational): Rational {
|
||||
val denominatorsGcd = gcd(denominator, other.denominator)
|
||||
val dividedThisDenominator = denominator / denominatorsGcd
|
||||
val dividedOtherDenominator = other.denominator / denominatorsGcd
|
||||
val numeratorCandidate = numerator * dividedOtherDenominator - dividedThisDenominator * other.numerator
|
||||
val secondGcd = gcd(numeratorCandidate, denominatorsGcd)
|
||||
return Rational(
|
||||
numeratorCandidate / secondGcd,
|
||||
dividedThisDenominator * (other.denominator / secondGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun minus(other: Int): Rational =
|
||||
Rational(
|
||||
numerator - denominator * other.toLong(),
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun minus(other: Long): Rational =
|
||||
Rational(
|
||||
numerator - denominator * other,
|
||||
denominator,
|
||||
toCheckInput = false
|
||||
)
|
||||
operator fun times(other: Rational): Rational {
|
||||
val thisDenominatorAndOtherNumeratorGcd = gcd(denominator, other.numerator)
|
||||
val otherDenominatorAndThisNumeratorGcd = gcd(other.denominator, numerator)
|
||||
return Rational(
|
||||
(numerator / otherDenominatorAndThisNumeratorGcd) * (other.numerator / thisDenominatorAndOtherNumeratorGcd),
|
||||
(denominator / thisDenominatorAndOtherNumeratorGcd) * (other.denominator / otherDenominatorAndThisNumeratorGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun times(other: Int): Rational {
|
||||
val other = other.toLong()
|
||||
val denominatorAndOtherGcd = gcd(denominator, other)
|
||||
return Rational(
|
||||
numerator * (other / denominatorAndOtherGcd),
|
||||
denominator / denominatorAndOtherGcd,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun times(other: Long): Rational {
|
||||
val denominatorAndOtherGcd = gcd(denominator, other)
|
||||
return Rational(
|
||||
numerator * (other / denominatorAndOtherGcd),
|
||||
denominator / denominatorAndOtherGcd,
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun div(other: Rational): Rational {
|
||||
val denominatorsGcd = gcd(denominator, other.denominator)
|
||||
val numeratorsGcd = gcd(numerator, other.numerator)
|
||||
return Rational(
|
||||
(numerator / numeratorsGcd) * (other.denominator / denominatorsGcd),
|
||||
(denominator / denominatorsGcd) * (other.numerator / numeratorsGcd)
|
||||
)
|
||||
}
|
||||
operator fun div(other: Int): Rational {
|
||||
val other = other.toLong()
|
||||
val numeratorAndOtherGcd = gcd(numerator, other)
|
||||
return Rational(
|
||||
numerator / numeratorAndOtherGcd,
|
||||
denominator * (other / numeratorAndOtherGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
operator fun div(other: Long): Rational {
|
||||
val numeratorAndOtherGcd = gcd(numerator, other)
|
||||
return Rational(
|
||||
numerator / numeratorAndOtherGcd,
|
||||
denominator * (other / numeratorAndOtherGcd),
|
||||
toCheckInput = false
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean =
|
||||
when (other) {
|
||||
is Rational -> numerator == other.numerator && denominator == other.denominator
|
||||
is Int -> numerator == other && denominator == 1L
|
||||
is Long -> numerator == other && denominator == 1L
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = 31 * numerator.hashCode() + denominator.hashCode()
|
||||
|
||||
override fun toString(): String = if (denominator == 1L) "$numerator" else "$numerator/$denominator"
|
||||
}
|
||||
|
||||
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE")
|
||||
@OptIn(UnstableKMathAPI::class)
|
||||
object RationalField : Field<Rational>, NumbersAddOps<Rational> {
|
||||
override inline val zero: Rational get() = Rational.ZERO
|
||||
override inline val one: Rational get() = Rational.ONE
|
||||
|
||||
override inline fun number(value: Number): Rational = Rational(value.toLong())
|
||||
|
||||
override inline fun add(left: Rational, right: Rational): Rational = left + right
|
||||
override inline fun multiply(left: Rational, right: Rational): Rational = left * right
|
||||
override inline fun divide(left: Rational, right: Rational): Rational = left / right
|
||||
override inline fun scale(a: Rational, value: Double): Rational = a * number(value)
|
||||
|
||||
override inline fun Rational.unaryMinus(): Rational = -this
|
||||
override inline fun Rational.plus(arg: Rational): Rational = this + arg
|
||||
override inline fun Rational.minus(arg: Rational): Rational = this - arg
|
||||
override inline fun Rational.times(arg: Rational): Rational = this * arg
|
||||
override inline fun Rational.div(arg: Rational): Rational = this / arg
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.functions.LabeledPolynomial
|
||||
import space.kscience.kmath.functions.LabeledRationalFunction
|
||||
import space.kscience.kmath.functions.NumberedPolynomial
|
||||
import space.kscience.kmath.functions.NumberedRationalFunction
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
|
||||
fun <T> assertContentEquals(
|
||||
expected: Map<T, Double>,
|
||||
actual: Map<T, Double>,
|
||||
absoluteTolerance: Double,
|
||||
message: String? = null
|
||||
) {
|
||||
assertEquals(expected.keys, actual.keys, message)
|
||||
for ((key, expectedValue) in expected) assertEquals(expectedValue, actual[key]!!, absoluteTolerance, message)
|
||||
}
|
||||
|
||||
fun assertEquals(
|
||||
expected: NumberedPolynomial<Double>,
|
||||
actual: NumberedPolynomial<Double>,
|
||||
absoluteTolerance: Double,
|
||||
message: String? = null
|
||||
) {
|
||||
assertContentEquals(
|
||||
expected.coefficients,
|
||||
actual.coefficients,
|
||||
absoluteTolerance,
|
||||
message
|
||||
)
|
||||
}
|
||||
|
||||
fun assertEquals(
|
||||
expected: LabeledPolynomial<Double>,
|
||||
actual: LabeledPolynomial<Double>,
|
||||
absoluteTolerance: Double,
|
||||
message: String? = null
|
||||
) {
|
||||
assertContentEquals(
|
||||
expected.coefficients,
|
||||
actual.coefficients,
|
||||
absoluteTolerance,
|
||||
message
|
||||
)
|
||||
}
|
||||
|
||||
fun assertEquals(
|
||||
expected: NumberedRationalFunction<Double>,
|
||||
actual: NumberedRationalFunction<Double>,
|
||||
absoluteTolerance: Double,
|
||||
message: String? = null
|
||||
) {
|
||||
assertEquals(
|
||||
expected.numerator,
|
||||
actual.numerator,
|
||||
absoluteTolerance,
|
||||
message
|
||||
)
|
||||
assertEquals(
|
||||
expected.denominator,
|
||||
actual.denominator,
|
||||
absoluteTolerance,
|
||||
message
|
||||
)
|
||||
}
|
||||
|
||||
fun assertEquals(
|
||||
expected: LabeledRationalFunction<Double>,
|
||||
actual: LabeledRationalFunction<Double>,
|
||||
absoluteTolerance: Double,
|
||||
message: String? = null
|
||||
) {
|
||||
assertEquals(
|
||||
expected.numerator,
|
||||
actual.numerator,
|
||||
absoluteTolerance,
|
||||
message
|
||||
)
|
||||
assertEquals(
|
||||
expected.denominator,
|
||||
actual.denominator,
|
||||
absoluteTolerance,
|
||||
message
|
||||
)
|
||||
}
|
||||
|
||||
inline fun <reified T : Throwable> assertFailsWithTypeAndMessage(
|
||||
expectedMessage: String? = null,
|
||||
assertionMessage: String? = null,
|
||||
block: () -> Unit
|
||||
) {
|
||||
assertEquals(
|
||||
expectedMessage,
|
||||
assertFailsWith(T::class, assertionMessage, block).message,
|
||||
assertionMessage
|
||||
)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.functions.testUtils
|
||||
|
||||
import space.kscience.kmath.expressions.Symbol
|
||||
import space.kscience.kmath.expressions.symbol
|
||||
|
||||
|
||||
val o: Rational = Rational(0)
|
||||
|
||||
val x: Symbol by symbol
|
||||
val y: Symbol by symbol
|
||||
val z: Symbol by symbol
|
||||
val t: Symbol by symbol
|
||||
val s: Symbol by symbol
|
||||
val iota: Symbol by symbol
|
@ -26,6 +26,7 @@ include(
|
||||
":kmath-core",
|
||||
":kmath-coroutines",
|
||||
":kmath-functions",
|
||||
":kmath-polynomial",
|
||||
":kmath-histograms",
|
||||
":kmath-commons",
|
||||
":kmath-viktor",
|
||||
|
Loading…
Reference in New Issue
Block a user