what did i do?
This commit is contained in:
15
polynomial-rs/.github/dependabot.yml
vendored
Normal file
15
polynomial-rs/.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "cargo" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
118
polynomial-rs/.github/workflows/rust-ci.yml
vendored
Normal file
118
polynomial-rs/.github/workflows/rust-ci.yml
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
name: Rust CI
|
||||
|
||||
on: push
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
rustfmt_check:
|
||||
name: Rustfmt Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
components: rustfmt
|
||||
override: true
|
||||
- name: rustfmt-check
|
||||
run: cargo fmt --all --check
|
||||
|
||||
clippy_check:
|
||||
name: Clippy Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check workflow permissions
|
||||
id: check_permissions
|
||||
uses: scherermichael-oss/action-has-permission@1.0.6
|
||||
with:
|
||||
required-permission: write
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run clippy action to produce annotations
|
||||
uses: actions-rs/clippy-check@v1
|
||||
if: ${{ steps.check_permissions.outputs.has-permission }}
|
||||
with:
|
||||
# GitHub displays the clippy job and its results as separate entries
|
||||
name: Clippy (stable) Results
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features --all-targets -- -D warnings
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
components: clippy
|
||||
override: true
|
||||
- name: Run clippy manually without annotations
|
||||
if: ${{ !steps.check_permissions.outputs.has-permission }}
|
||||
run: cargo clippy --all-features --all-targets -- -D warnings
|
||||
|
||||
security_audit:
|
||||
name: Security Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- stable
|
||||
- 1.56.1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: cargo test
|
||||
run: cargo test --workspace --all-targets --all-features
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- stable
|
||||
- 1.56.1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
profile: minimal
|
||||
override: true
|
||||
- name: cargo build
|
||||
run: cargo build --workspace --all-targets --all-features
|
||||
|
||||
coverage:
|
||||
name: Code coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
components: llvm-tools-preview
|
||||
- name: Install cargo-llvm-cov
|
||||
uses: taiki-e/install-action@cargo-llvm-cov
|
||||
- name: Generate code coverage
|
||||
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: lcov.info
|
||||
fail_ci_if_error: true
|
||||
|
2
polynomial-rs/.gitignore
vendored
Normal file
2
polynomial-rs/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
/Cargo.lock
|
26
polynomial-rs/Cargo.toml
Normal file
26
polynomial-rs/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "polynomial"
|
||||
version = "0.2.5"
|
||||
edition = "2021"
|
||||
rust-version = "1.56.1"
|
||||
authors = ["gifnksm <makoto.nksm+github@gmail.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/gifnksm/polynomial-rs"
|
||||
description = "A library for manipulating polynomials"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "passively-maintained" }
|
||||
|
||||
[dependencies]
|
||||
num-traits = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
modinverse = "0.1.1"
|
||||
|
||||
[features]
|
||||
serde = ["dep:serde"]
|
||||
|
||||
[package.metadata.release]
|
||||
pre-release-replacements = [
|
||||
{ file = "README.md", search = "polynomial = \"[0-9\\.]+\"", replace = "{{crate_name}} = \"{{version}}\"" }
|
||||
]
|
22
polynomial-rs/LICENSE
Normal file
22
polynomial-rs/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 NAKASHIMA, Makoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
30
polynomial-rs/README.md
Normal file
30
polynomial-rs/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# polynomial-rs
|
||||
|
||||
[](https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section)
|
||||
[](LICENSE)
|
||||
[](https://crates.io/crates/polynomial)
|
||||
[](https://docs.rs/polynomial/latest/)
|
||||
[](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field)
|
||||
[](https://github.com/gifnksm/polynomial-rs/actions/workflows/rust-ci.yml)
|
||||
[](https://codecov.io/gh/gifnksm/polynomial-rs)
|
||||
|
||||
A library for manipulating polynomials.
|
||||
|
||||
[Documentation](https://docs.rs/polynomial/latest/polynomial/)
|
||||
|
||||
## How to use?
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
polynomial = "0.2.5"
|
||||
```
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
The minimum supported Rust version is **Rust 1.56.1**.
|
||||
At least the last 3 versions of stable Rust are supported at any given time.
|
||||
|
||||
While a crate is pre-release status (0.x.x) it may have its MSRV bumped in a patch release.
|
||||
Once a crate has reached 1.x, any MSRV bump will be accompanied with a new minor version.
|
616
polynomial-rs/src/lib.rs
Normal file
616
polynomial-rs/src/lib.rs
Normal file
@ -0,0 +1,616 @@
|
||||
//! A library for manipulating polynomials.
|
||||
|
||||
#![warn(bad_style)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(trivial_numeric_casts)]
|
||||
#![warn(unused)]
|
||||
#![warn(unused_extern_crates)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
#![warn(unused_results)]
|
||||
|
||||
use num_traits::{FromPrimitive, One, Zero};
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub, Rem};
|
||||
use std::{cmp, fmt};
|
||||
|
||||
/// A polynomial.
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Polynomial<T> {
|
||||
data: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T: Zero> Polynomial<T> {
|
||||
/// Creates a new `Polynomial` from a `Vec` of coefficients.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use polynomial::Polynomial;
|
||||
/// let poly = Polynomial::new(vec![1, 2, 3]);
|
||||
/// assert_eq!("1+2*x+3*x^2", poly.pretty("x"));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new(mut data: Vec<T>) -> Self {
|
||||
while let Some(true) = data.last().map(|x| x.is_zero()) {
|
||||
let _ = data.pop();
|
||||
}
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Polynomial<T>
|
||||
where
|
||||
T: One
|
||||
+ Zero
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ Neg<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Rem<Output = T>
|
||||
+ Ord
|
||||
+ fmt::Display,
|
||||
{
|
||||
fn egcd(a: T, b: T) -> (T, T, T) {
|
||||
if a == T::zero() {
|
||||
(b, T::zero(), T::one())
|
||||
}
|
||||
else {
|
||||
let (g, x, y) = Self::egcd(b % a, a);
|
||||
(g, y - (b / a) * x, x)
|
||||
}
|
||||
}
|
||||
|
||||
fn modinverse(a: T, m: T) -> Option<T> {
|
||||
let (g, x, _) = Self::egcd(a, m);
|
||||
if g != T::one() {
|
||||
None
|
||||
}
|
||||
else {
|
||||
Some((x % m + m) % m)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the [Lagrange polynomial] that fits a number of points.
|
||||
///
|
||||
/// [Lagrange polynomial]: https://en.wikipedia.org/wiki/Lagrange_polynomial
|
||||
///
|
||||
/// Returns `None` if any two x-coordinates are the same.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use polynomial::Polynomial;
|
||||
/// let poly = Polynomial::lagrange(&[1, 2, 3], &[10, 40, 90]).unwrap();
|
||||
/// println!("{}", poly.pretty("x"));
|
||||
/// assert_eq!("10*x^2", poly.pretty("x"));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn lagrange(xs: &[T], ys: &[T], field: Option<&T>) -> Option<Self> {
|
||||
let mut res = Polynomial::new(vec![Zero::zero()]);
|
||||
for ((i, x), y) in (0..).zip(xs.iter()).zip(ys.iter()) {
|
||||
let mut p: Polynomial<T> = Polynomial::new(vec![T::one()]);
|
||||
let mut denom = T::one();
|
||||
let mut negative = false;
|
||||
for (j, x2) in (0..).zip(xs.iter()) {
|
||||
if i != j {
|
||||
p = p * &Polynomial::new(vec![-x2.clone(), T::one()]);
|
||||
let diff = if x < x2 {
|
||||
x2.clone() - x.clone()
|
||||
} else {
|
||||
x.clone() - x2.clone()
|
||||
};
|
||||
if diff.is_zero() {
|
||||
return None;
|
||||
}
|
||||
|
||||
negative = !(negative ^ (x < x2));
|
||||
denom = denom * diff;
|
||||
}
|
||||
}
|
||||
let scalar = match field {
|
||||
Some(f) => {
|
||||
let denom_inv = Self::modinverse(denom, f.clone()).unwrap();
|
||||
if negative {
|
||||
- y.clone() * denom_inv
|
||||
} else {
|
||||
y.clone() * denom_inv
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if negative {
|
||||
- y.clone() / denom
|
||||
} else {
|
||||
y.clone() / denom
|
||||
}
|
||||
}
|
||||
};
|
||||
println!("{}/{} = {}", y.to_string(), denom.to_string(), scalar);
|
||||
res = res + p * &Polynomial::<T>::new(vec![scalar]);
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Polynomial<T>
|
||||
where
|
||||
T: One
|
||||
+ Zero
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ Neg<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ Mul<Output = T>
|
||||
+ Sub<Output = T>
|
||||
+ Rem<Output = T>
|
||||
+ Ord
|
||||
+ FromPrimitive
|
||||
+ fmt::Display,
|
||||
{
|
||||
/// [Chebyshev approximation] fits a function to a polynomial over a range of values.
|
||||
///
|
||||
/// [Chebyshev approximation]: https://en.wikipedia.org/wiki/Approximation_theory#Chebyshev_approximation
|
||||
///
|
||||
/// This attempts to minimize the maximum error.
|
||||
///
|
||||
/// Retrurns `None` if `n < 1` or `xmin >= xmax`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use polynomial::Polynomial;
|
||||
/// use std::f64::consts::PI;
|
||||
/// let p = Polynomial::chebyshev(&f64::sin, 7, -PI/4., PI/4.).unwrap();
|
||||
/// assert!((p.eval(0.) - (0.0_f64).sin()).abs() < 0.0001);
|
||||
/// assert!((p.eval(0.1) - (0.1_f64).sin()).abs() < 0.0001);
|
||||
/// assert!((p.eval(-0.1) - (-0.1_f64).sin()).abs() < 0.0001);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn chebyshev<F: Fn(T) -> T>(f: &F, n: usize, xmin: f64, xmax: f64) -> Option<Self> {
|
||||
if n < 1 || xmin >= xmax {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut xs = Vec::new();
|
||||
for i in 0..n {
|
||||
use std::f64::consts::PI;
|
||||
let x = T::from_f64(
|
||||
(xmax + xmin) * 0.5
|
||||
+ (xmin - xmax) * 0.5 * ((2 * i + 1) as f64 * PI / (2 * n) as f64).cos(),
|
||||
)
|
||||
.unwrap();
|
||||
xs.push(x);
|
||||
}
|
||||
|
||||
let ys: Vec<T> = xs.iter().map(|x| f(x.clone())).collect();
|
||||
Polynomial::lagrange(&xs[0..], &ys[0..], None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + Mul<Output = T> + Clone> Polynomial<T> {
|
||||
/// Evaluates the polynomial at a point.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use polynomial::Polynomial;
|
||||
/// let poly = Polynomial::new(vec![1, 2, 3]);
|
||||
/// assert_eq!(1, poly.eval(0));
|
||||
/// assert_eq!(6, poly.eval(1));
|
||||
/// assert_eq!(17, poly.eval(2));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn eval(&self, x: T) -> T {
|
||||
let mut result: T = Zero::zero();
|
||||
for n in self.data.iter().rev() {
|
||||
result = n.clone() + result * x.clone();
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Polynomial<T> {
|
||||
/// Gets the slice of internal data.
|
||||
#[inline]
|
||||
pub fn data(&self) -> &[T] {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Polynomial<T>
|
||||
where
|
||||
T: Zero + One + Eq + Neg<Output = T> + Ord + fmt::Display + Clone,
|
||||
{
|
||||
/// Pretty prints the polynomial.
|
||||
pub fn pretty(&self, x: &str) -> String {
|
||||
if self.is_zero() {
|
||||
return "0".to_string();
|
||||
}
|
||||
|
||||
let one = One::one();
|
||||
let mut s = Vec::new();
|
||||
for (i, n) in self.data.iter().enumerate() {
|
||||
// output n*x^i / -n*x^i
|
||||
if n.is_zero() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let term = if i.is_zero() {
|
||||
n.to_string()
|
||||
} else if i == 1 {
|
||||
if (*n) == one {
|
||||
x.to_string()
|
||||
} else if (*n) == -one.clone() {
|
||||
format!("-{}", x)
|
||||
} else {
|
||||
format!("{}*{}", n, x)
|
||||
}
|
||||
} else if (*n) == one {
|
||||
format!("{}^{}", x, i)
|
||||
} else if (*n) == -one.clone() {
|
||||
format!("-{}^{}", x, i)
|
||||
} else {
|
||||
format!("{}*{}^{}", n, x, i)
|
||||
};
|
||||
|
||||
if !s.is_empty() && (*n) > Zero::zero() {
|
||||
s.push("+".to_string());
|
||||
}
|
||||
s.push(term);
|
||||
}
|
||||
|
||||
s.concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Neg for Polynomial<T>
|
||||
where
|
||||
T: Neg + Zero + Clone,
|
||||
<T as Neg>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<T as Neg>::Output>;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Polynomial<<T as Neg>::Output> {
|
||||
-&self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Neg for &'a Polynomial<T>
|
||||
where
|
||||
T: Neg + Zero + Clone,
|
||||
<T as Neg>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<T as Neg>::Output>;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Polynomial<<T as Neg>::Output> {
|
||||
Polynomial::new(self.data.iter().map(|x| -x.clone()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! forward_val_val_binop {
|
||||
(impl $imp:ident, $method:ident) => {
|
||||
impl<Lhs, Rhs> $imp<Polynomial<Rhs>> for Polynomial<Lhs>
|
||||
where
|
||||
Lhs: Zero + $imp<Rhs> + Clone,
|
||||
Rhs: Zero + Clone,
|
||||
<Lhs as $imp<Rhs>>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<Lhs as $imp<Rhs>>::Output>;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, other: Polynomial<Rhs>) -> Polynomial<<Lhs as $imp<Rhs>>::Output> {
|
||||
(&self).$method(&other)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! forward_ref_val_binop {
|
||||
(impl $imp:ident, $method:ident) => {
|
||||
impl<'a, Lhs, Rhs> $imp<Polynomial<Rhs>> for &'a Polynomial<Lhs>
|
||||
where
|
||||
Lhs: Zero + $imp<Rhs> + Clone,
|
||||
Rhs: Zero + Clone,
|
||||
<Lhs as $imp<Rhs>>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<Lhs as $imp<Rhs>>::Output>;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, other: Polynomial<Rhs>) -> Polynomial<<Lhs as $imp<Rhs>>::Output> {
|
||||
self.$method(&other)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! forward_val_ref_binop {
|
||||
(impl $imp:ident, $method:ident) => {
|
||||
impl<'a, Lhs, Rhs> $imp<&'a Polynomial<Rhs>> for Polynomial<Lhs>
|
||||
where
|
||||
Lhs: Zero + $imp<Rhs> + Clone,
|
||||
Rhs: Zero + Clone,
|
||||
<Lhs as $imp<Rhs>>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<Lhs as $imp<Rhs>>::Output>;
|
||||
|
||||
#[inline]
|
||||
fn $method(self, other: &Polynomial<Rhs>) -> Polynomial<<Lhs as $imp<Rhs>>::Output> {
|
||||
(&self).$method(other)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! forward_all_binop {
|
||||
(impl $imp:ident, $method:ident) => {
|
||||
forward_val_val_binop!(impl $imp, $method);
|
||||
forward_ref_val_binop!(impl $imp, $method);
|
||||
forward_val_ref_binop!(impl $imp, $method);
|
||||
};
|
||||
}
|
||||
|
||||
forward_all_binop!(impl Add, add);
|
||||
|
||||
impl<'a, 'b, Lhs, Rhs> Add<&'b Polynomial<Rhs>> for &'a Polynomial<Lhs>
|
||||
where
|
||||
Lhs: Zero + Add<Rhs> + Clone,
|
||||
Rhs: Zero + Clone,
|
||||
<Lhs as Add<Rhs>>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<Lhs as Add<Rhs>>::Output>;
|
||||
|
||||
fn add(self, other: &Polynomial<Rhs>) -> Polynomial<<Lhs as Add<Rhs>>::Output> {
|
||||
let max_len = cmp::max(self.data.len(), other.data.len());
|
||||
let min_len = cmp::min(self.data.len(), other.data.len());
|
||||
|
||||
let mut sum = Vec::with_capacity(max_len);
|
||||
for i in 0..min_len {
|
||||
sum.push(self.data[i].clone() + other.data[i].clone());
|
||||
}
|
||||
|
||||
if self.data.len() <= other.data.len() {
|
||||
for i in min_len..max_len {
|
||||
sum.push(num_traits::zero::<Lhs>() + other.data[i].clone());
|
||||
}
|
||||
} else {
|
||||
for i in min_len..max_len {
|
||||
sum.push(self.data[i].clone() + num_traits::zero::<Rhs>());
|
||||
}
|
||||
}
|
||||
|
||||
Polynomial::new(sum)
|
||||
}
|
||||
}
|
||||
|
||||
forward_all_binop!(impl Sub, sub);
|
||||
|
||||
impl<'a, 'b, Lhs, Rhs> Sub<&'b Polynomial<Rhs>> for &'a Polynomial<Lhs>
|
||||
where
|
||||
Lhs: Zero + Sub<Rhs> + Clone,
|
||||
Rhs: Zero + Clone,
|
||||
<Lhs as Sub<Rhs>>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<Lhs as Sub<Rhs>>::Output>;
|
||||
|
||||
fn sub(self, other: &Polynomial<Rhs>) -> Polynomial<<Lhs as Sub<Rhs>>::Output> {
|
||||
let min_len = cmp::min(self.data.len(), other.data.len());
|
||||
let max_len = cmp::max(self.data.len(), other.data.len());
|
||||
|
||||
let mut sub = Vec::with_capacity(max_len);
|
||||
for i in 0..min_len {
|
||||
sub.push(self.data[i].clone() - other.data[i].clone());
|
||||
}
|
||||
if self.data.len() <= other.data.len() {
|
||||
for i in min_len..max_len {
|
||||
sub.push(num_traits::zero::<Lhs>() - other.data[i].clone())
|
||||
}
|
||||
} else {
|
||||
for i in min_len..max_len {
|
||||
sub.push(self.data[i].clone() - num_traits::zero::<Rhs>())
|
||||
}
|
||||
}
|
||||
Polynomial::new(sub)
|
||||
}
|
||||
}
|
||||
|
||||
forward_all_binop!(impl Mul, mul);
|
||||
|
||||
impl<'a, 'b, Lhs, Rhs> Mul<&'b Polynomial<Rhs>> for &'a Polynomial<Lhs>
|
||||
where
|
||||
Lhs: Zero + Mul<Rhs> + Clone,
|
||||
Rhs: Zero + Clone,
|
||||
<Lhs as Mul<Rhs>>::Output: Zero,
|
||||
{
|
||||
type Output = Polynomial<<Lhs as Mul<Rhs>>::Output>;
|
||||
|
||||
fn mul(self, other: &Polynomial<Rhs>) -> Polynomial<<Lhs as Mul<Rhs>>::Output> {
|
||||
if self.is_zero() || other.is_zero() {
|
||||
return Polynomial::new(vec![]);
|
||||
}
|
||||
|
||||
let slen = self.data.len();
|
||||
let olen = other.data.len();
|
||||
let prod = (0..slen + olen - 1)
|
||||
.map(|i| {
|
||||
let mut p = num_traits::zero::<<Lhs as Mul<Rhs>>::Output>();
|
||||
let kstart = cmp::max(olen, i + 1) - olen;
|
||||
let kend = cmp::min(slen, i + 1);
|
||||
for k in kstart..kend {
|
||||
p = p + self.data[k].clone() * other.data[i - k].clone();
|
||||
}
|
||||
p
|
||||
})
|
||||
.collect();
|
||||
Polynomial::new(prod)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + Clone> Zero for Polynomial<T> {
|
||||
#[inline]
|
||||
fn zero() -> Self {
|
||||
Self { data: vec![] }
|
||||
}
|
||||
#[inline]
|
||||
fn is_zero(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero + One + Clone> One for Polynomial<T> {
|
||||
#[inline]
|
||||
fn one() -> Self {
|
||||
Self {
|
||||
data: vec![One::one()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Polynomial;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
fn check(dst: Vec<i32>, src: Vec<i32>) {
|
||||
assert_eq!(dst, Polynomial::new(src).data);
|
||||
}
|
||||
check(vec![1, 2, 3], vec![1, 2, 3]);
|
||||
check(vec![1, 2, 3], vec![1, 2, 3, 0, 0]);
|
||||
check(vec![], vec![0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn neg_add_sub() {
|
||||
fn check(a: &[i32], b: &[i32], c: &[i32]) {
|
||||
fn check_eq(a: &Polynomial<i32>, b: &Polynomial<i32>) {
|
||||
assert_eq!(*a, *b);
|
||||
assert_eq!(-a, -b);
|
||||
}
|
||||
fn check_add(sum: &Polynomial<i32>, a: &Polynomial<i32>, b: &Polynomial<i32>) {
|
||||
check_eq(sum, &(a + b));
|
||||
check_eq(sum, &(b + a));
|
||||
}
|
||||
fn check_sub(sum: &Polynomial<i32>, a: &Polynomial<i32>, b: &Polynomial<i32>) {
|
||||
check_eq(a, &(sum - b));
|
||||
check_eq(b, &(sum - a));
|
||||
}
|
||||
|
||||
let a = &Polynomial::new(a.to_vec());
|
||||
let b = &Polynomial::new(b.to_vec());
|
||||
let c = &Polynomial::new(c.to_vec());
|
||||
check_add(c, a, b);
|
||||
check_add(&(-c), &(-a), &(-b));
|
||||
check_sub(c, a, b);
|
||||
check_sub(&(-c), &(-a), &(-b));
|
||||
}
|
||||
check(&[], &[], &[]);
|
||||
check(&[], &[1], &[1]);
|
||||
check(&[1], &[1], &[2]);
|
||||
check(&[1, 0, 1], &[1], &[2, 0, 1]);
|
||||
check(&[1, 0, -1], &[-1, 0, 1], &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mul() {
|
||||
fn check(a: &[i32], b: &[i32], c: &[i32]) {
|
||||
let a = Polynomial::new(a.to_vec());
|
||||
let b = Polynomial::new(b.to_vec());
|
||||
let c = Polynomial::new(c.to_vec());
|
||||
assert_eq!(c, &a * &b);
|
||||
assert_eq!(c, &b * &a);
|
||||
}
|
||||
check(&[], &[], &[]);
|
||||
check(&[0, 0], &[], &[]);
|
||||
check(&[0, 0], &[1], &[]);
|
||||
check(&[1, 0], &[1], &[1]);
|
||||
check(&[1, 0, 1], &[1], &[1, 0, 1]);
|
||||
check(&[1, 1], &[1, 1], &[1, 2, 1]);
|
||||
check(&[1, 1], &[1, 0, 1], &[1, 1, 1, 1]);
|
||||
check(&[0, 0, 1], &[0, 0, 1], &[0, 0, 0, 0, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval() {
|
||||
fn check<F: Fn(i32) -> i32>(pol: &[i32], f: F) {
|
||||
for n in -10..10 {
|
||||
assert_eq!(f(n), Polynomial::new(pol.to_vec()).eval(n));
|
||||
}
|
||||
}
|
||||
check(&[], |_x| 0);
|
||||
check(&[1], |_x| 1);
|
||||
check(&[1, 1], |x| x + 1);
|
||||
check(&[0, 1], |x| x);
|
||||
check(&[10, -10, 10], |x| 10 * x * x - 10 * x + 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pretty() {
|
||||
fn check(slice: &[i32], s: &str) {
|
||||
assert_eq!(s.to_string(), Polynomial::new(slice.to_vec()).pretty("x"));
|
||||
}
|
||||
check(&[0], "0");
|
||||
check(&[1], "1");
|
||||
check(&[1, 1], "1+x");
|
||||
check(&[1, 1, 1], "1+x+x^2");
|
||||
check(&[2, 2, 2], "2+2*x+2*x^2");
|
||||
check(&[0, 0, 0, 1], "x^3");
|
||||
check(&[0, 0, 0, -1], "-x^3");
|
||||
check(&[-1, 0, 0, -1], "-1-x^3");
|
||||
check(&[-1, 1, 0, -1], "-1+x-x^3");
|
||||
check(&[-1, 1, -1, -1], "-1+x-x^2-x^3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lagrange() {
|
||||
// Evaluate the lagrange polynomial at the x coordinates.
|
||||
// The error should be close to zero.
|
||||
fn check(xs: &[f64], ys: &[f64]) {
|
||||
let p = Polynomial::lagrange(xs, ys).unwrap();
|
||||
for (x, y) in xs.iter().zip(ys) {
|
||||
assert!((p.eval(*x) - y).abs() < 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
// Squares
|
||||
check(&[1., 2., 3.], &[10., 40., 90.]);
|
||||
// Cubes
|
||||
check(&[-1., 0., 1., 2.], &[-1000., 0., 1000., 8000.]);
|
||||
// Non linear x.
|
||||
check(&[1., 9., 10., 11.], &[1., 2., 3., 4.]);
|
||||
// Test double x failure case.
|
||||
assert_eq!(
|
||||
Polynomial::lagrange(&[1., 9., 9., 11.], &[1., 2., 3., 4.]),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chebyshev() {
|
||||
// Construct a Chebyshev approximation for a function
|
||||
// and evaulate it at 100 points.
|
||||
fn check<F: Fn(f64) -> f64>(f: &F, n: usize, xmin: f64, xmax: f64) {
|
||||
let p = Polynomial::chebyshev(f, n, xmin, xmax).unwrap();
|
||||
for i in 0..=100 {
|
||||
let x = xmin + (i as f64) * ((xmax - xmin) / 100.0);
|
||||
let diff = (f(x) - p.eval(x)).abs();
|
||||
assert!(diff < 0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
// Approximate some common functions.
|
||||
use std::f64::consts::PI;
|
||||
check(&f64::sin, 7, -PI / 2., PI / 2.);
|
||||
check(&f64::cos, 7, 0., PI / 4.);
|
||||
|
||||
// Test n >= 1 condition
|
||||
assert!(Polynomial::chebyshev(&f64::exp, 0, 0., 1.) == None);
|
||||
|
||||
// Test xmax > xmin condition
|
||||
assert!(Polynomial::chebyshev(&f64::ln, 1, 1., 0.) == None);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user