1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// @adjivas - github.com/adjivas. See the LICENSE
// file at the top-level directory of this distribution and at
// https://github.com/adjivas/computor-v1
//
// This file may not be copied, modified, or distributed

//! This module `polynomial` is a overload of `Order`'s interface.

pub mod order;
pub mod non_zero_constant;
pub mod linear;
pub mod quadratic;

use self::order::term::degree::Degree;
use self::order::term::Term;
use self::order::Order;
use self::non_zero_constant::NonZeroConstant;
use self::linear::Linear;
use self::quadratic::Quadratic;

use ::array::FixedSizeArray;

use ::fmt;
use ::str;
use ::num;
use ::num2;

use ops::{Not, Neg, Mul, Div, Sub, Add, BitAnd};

use num2::Zero;

/// The Polynomial is a overload of `Order`'s interface.
#[derive(Debug, Clone)]
pub struct Polynomial(Order);

impl Polynomial {
    pub fn is_oob(&self) -> bool {
        self.get_non_zero_constant()
            .is_some()
            .bitand(self.get_linear()
                        .is_some())
            .bitand(self.get_quadratic()
                        .is_some())
            .not()
    }

    pub fn get_non_zero_constant(&self) -> Option<NonZeroConstant> {
        match self.0.as_slice() {
            &[Term::Determinate(_)] | &[] => {
                if self.0.is_empty() {
                    Some(NonZeroConstant::SolubleEverywhere)
                } else {
                    Some(NonZeroConstant::Unsoluble)
                }
            },
            _ => None,
        }
    }

    pub fn get_linear(&self) -> Option<Linear> {
        match self.0.as_slice() {
            &[Term::Indeterminate(a, _),
              Term::Determinate(b)] => {
                match (a, b) {
                    ab if ab.eq(&(f64::zero(), f64::zero())) => Some(Linear::SolubleEverywhere),
                    (a, _) if a.eq(&f64::zero()) => Some(Linear::Unsoluble),
                    (a, b) => Some(Linear::Soluble(b.neg().div(a))),
                }
            }
            _ => None,
        }
    }

    pub fn get_quadratic(&self) -> Option<Quadratic> {
        match self.0.as_slice() {
            &[Term::Indeterminate(a, Degree(2)),
              Term::Indeterminate(b, Degree(1)),
              Term::Determinate(c)] if a != 0f64 => {
                match num2::pow::pow(b, 2).sub(a.mul(c).mul(4.0)) {
                    delta if delta < f64::zero() => Some(Quadratic::Negative(a, b)),
                    delta if delta > f64::zero() => Some(Quadratic::Positive(delta.sqrt().sub(b).div(a.mul(2.0)),
                                                                     delta.sqrt().add(b).neg().div(a.mul(2.0)))),
                    _ => Some(Quadratic::Null(b.neg().div(a.mul(2.0)))),
                }
            },
            _ => None,
        }
    }
}

impl str::FromStr for Polynomial {
    type Err = num::ParseIntError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Polynomial(try!(Order::from_str(s))))
    }
}

impl Default for Polynomial {
    fn default() -> Self {
        Polynomial(Order::default())
    }
}

impl fmt::Display for Polynomial {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Reduced form: {}\n", self.0));
        match self.0.as_slice() {
            &[Term::Indeterminate(a, Degree(2)),
              Term::Indeterminate(b, Degree(1)),
              Term::Determinate(c)] if a.ne(&f64::zero()) => {
                try!(write!(f, "Polynomial degree: 2\n"));
                let delta = num2::pow::pow(b, 2).sub(a.mul(c).mul(4.0));
                try!(write!(f, "Delta: b^2-4ac={b}^2-4*{a}*{c}={d}\n",
                                a = a,
                                b = b,
                                c = c,
                                d = delta));
                match delta {
                    delta if delta < f64::zero() => { 
                        write!(f, "Discriminant is strictly negative, the two complex solutions are:\n\
                                   (-b+sqrt(|delta|))/(a*2)=({b}+i*sqrt(|{d}|))/({a}*2)\n\
                                   (-b-sqrt(|delta|))/(a*2)=({b}-i*sqrt(|{d}|))/({a}*2)\n",
                                   a = a,
                                   b = b.neg(),
                                   d = delta)
                    },
                    delta if delta > f64::zero() => {
                        write!(f, "Discriminant is strictly positive, the two real solutions are:\n\
                                   (-b+sqrt(delta))/(a*2)=({b}+sqrt({d}))/({a}*2)={x1}\n\
                                   (-b-sqrt(delta))/(a*2)=({b}-sqrt({d}))/({a}*2)={x2}\n",
                                   a = a,
                                   b = b.neg(),
                                   d = delta,
                                   x1 = delta.sqrt().sub(b).div(a.mul(2.0)),
                                   x2 = delta.sqrt().add(b).neg().div(a.mul(2.0)))
                    },
                    _ => {
                        write!(f, "Discriminant is null, the real solution is:\n\
                                   -b/2a={b}/(2*{a})={x}\n",
                                   b = b.neg(),
                                   a = a,
                                   x = b.neg().div(a.mul(2.0)))
                    },
                }
            },
            &[Term::Indeterminate(a, _),
              Term::Determinate(b)] => {
                try!(write!(f, "Polynomial degree: 1\n"));
                match (a, b) {
                    ab if ab.eq(&(f64::zero(), f64::zero())) => write!(f, "All the real number are solutions.\n"), 
                    (a, _) if a.eq(&f64::zero()) => write!(f, "There isn't any solution.\n"), 
                    (a, b) => write!(f, "The solution is:\n\
                                         -a/b=-{b}/{a}={x}\n",
                                          b = b,
                                          a = a,
                                          x = b.neg().div(a)),
                }
            },
            &[Term::Determinate(_)] | &[] => {
                try!(write!(f, "Polynomial degree: 0\n"));
                if self.0.is_empty() {
                    write!(f, "All the real number are solutions.\n")
                } else {
                    write!(f, "There isn't any solution.\n")
                }
            },
            _ => write!(f, "Out of supported polynomes.\n"),
        }
    }
}