Repository: hayden4r4/blackscholes-rust (language: python)

Issue:
add f64 support
I think it should be self-explanatory what is happening here, but let me know if you have any questions.

I made some design choices around templating in some places and using macros for implementation in others, but I could go either way on which makes more sense

This may break some public API (most notably `Inputs` is templated on type now, which wasn't true before), but I expect most existing code would be able to infer the old f32 type without code changes.


Discussion / hints:
just getting back to this -- I rebased my changes over the recent commits and am planning to address the comments, but I guess my first question is: is #13 going to obviate the need for this PR? or is this still useful?
Hey @claireratigan,

I would like to move forward with #13. In my opinion, implementing Rust in bs is necessary. Your approach is what I would like to pursue in the next steps.

Adding multiple options to calculate values using f32/f64, and making the trait flexible enough to support U256, is essential.

Please allow some time for @hayden4r4 to review and accept it :D 
Hey @claireratigan ,
We are almost there. If you are still interested in introduce support multi types, we are open for that :) 

The best solution will be generic functions if it is possible of course.
@day01 I'm not sure I understood your last comment exactly, but I think if you look at my latest commit it contains what you're looking for :)
@claireratigan yes it contains everything what i was looking for :)
can you add some tests to affected functions?  
I think this is ready for another look
we have still error in: `benches/implied_volatility.rs`
`const INPUTS: Inputs = Inputs {`

And it will be all :)

Now that the other PR is closed I want to make the `lets_be_rational` functions generic as well, which is going to take a bit more work

Also, for what it's worth,  after I rebased over the new changes my benchmark test increased ~20%:

```
     Running benches/implied_volatility.rs (target/release/deps/implied_volatility-12094a8984b9c1f1)
Gnuplot not found, using plotters backend
calc_rational_iv        time:   [310.61 ns 311.32 ns 312.21 ns]
                        change: [+21.417% +21.736% +22.030%] (p = 0.00 < 0.05)
                        Performance has regressed.
```
templating the lets_be_rational_functions improved the benchmarks, not quite enough to overcome the total regression:

```
calc_rational_iv        time:   [267.38 ns 267.55 ns 267.82 ns]
                        change: [-13.822% -13.669% -13.508%] (p = 0.00 < 0.05)
                        Performance has improved.
```
Verified, tests are correct, f32 correct. 
@claireratigan Unfortunately I have to revert your solution to f64 support only. based on implementation of lets be rational we have taylor series n newton raphson method adjusted to f64 precision. (17th nr n 12th taylor) and i forgot about it...

To prepare correct Float generic operation we have to change lower algo for example to add f128 support.
Of course all your work to adjust to f64 will stay in repo. 
n of course in lib it was a bug that precision was for f32. all calculation was adjusted to f64.

Fix patch:
diff --git a/benches/implied_volatility.rs b/benches/implied_volatility.rs
index 1686d42..223d4c1 100644
--- a/benches/implied_volatility.rs
+++ b/benches/implied_volatility.rs
@@ -1,7 +1,7 @@
 use blackscholes::{ImpliedVolatility, Inputs, OptionType};
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
 
-const INPUTS: Inputs<f64> = Inputs {
+const INPUTS: Inputs = Inputs {
     option_type: OptionType::Call,
     s: 51.03,
     k: 55.0,
diff --git a/benches/pricing.rs b/benches/pricing.rs
index 815dde0..cacc4d0 100644
--- a/benches/pricing.rs
+++ b/benches/pricing.rs
@@ -1,7 +1,7 @@
 use blackscholes::{Inputs, OptionType, Pricing};
 use criterion::{black_box, criterion_group, criterion_main, Criterion};
 
-const INPUTS: Inputs<f64> = Inputs {
+const INPUTS: Inputs = Inputs {
     option_type: OptionType::Call,
     s: 51.03,
     k: 55.0,
diff --git a/src/greeks.rs b/src/greeks.rs
index cce19b3..f899378 100644
--- a/src/greeks.rs
+++ b/src/greeks.rs
@@ -1,10 +1,8 @@
-use num_traits::{AsPrimitive, Float, FloatConst, FromPrimitive};
 use std::collections::HashMap;
 
-use crate::{
-    calc_d1d2, calc_nd1nd2, calc_nprimed1, calc_nprimed2, Inputs, OptionType, Pricing,
-    DAYS_PER_YEAR,
-};
+use num_traits::Float;
+
+use crate::{Inputs, OptionType, Pricing, *};
 
 pub trait Greeks<T>: Pricing<T>
 where
@@ -30,26 +28,23 @@ where
     fn calc_all_greeks(&self) -> Result<HashMap<String, T>, String>;
 }
 
-impl<T> Greeks<T> for Inputs<T>
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
+impl Greeks<f32> for Inputs {
     /// Calculates the delta of the option.
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the delta of the option.
+    /// f32 of the delta of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let delta = inputs.calc_delta().unwrap();
     /// ```
-    fn calc_delta(&self) -> Result<T, String> {
-        let (nd1, _): (T, T) = calc_nd1nd2(self)?;
+    fn calc_delta(&self) -> Result<f32, String> {
+        let (nd1, _): (f32, f32) = calc_nd1nd2(self)?;
 
-        let option_type: T = T::from::<f64>(self.option_type.into()).unwrap();
-        let delta = option_type * T::E().powf(-self.q * self.t) * nd1;
+        let option_type: f64 = self.option_type.into();
+        let delta = option_type as f32 * E.powf(-self.q * self.t) * nd1;
 
         Ok(delta)
     }
@@ -58,20 +53,20 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the gamma of the option.
+    /// f32 of the gamma of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let gamma = inputs.calc_gamma().unwrap();
     /// ```
-    fn calc_gamma(&self) -> Result<T, String> {
+    fn calc_gamma(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
 
-        let nprimed1: T = calc_nprimed1(self)?;
-        let gamma: T = T::E().powf(-self.q * self.t) * nprimed1 / (self.s * sigma * self.t.sqrt());
+        let nprimed1: f32 = calc_nprimed1(self)?;
+        let gamma: f32 = E.powf(-self.q * self.t) * nprimed1 / (self.s * sigma * self.t.sqrt());
         Ok(gamma)
     }
 
@@ -80,28 +75,28 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of theta per day (not per year).
+    /// f32 of theta per day (not per year).
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let theta = inputs.calc_theta().unwrap();
     /// ```
-    fn calc_theta(&self) -> Result<T, String> {
+    fn calc_theta(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
 
-        let nprimed1: T = calc_nprimed1(self)?;
-        let (nd1, nd2): (T, T) = calc_nd1nd2(self)?;
+        let nprimed1: f32 = calc_nprimed1(self)?;
+        let (nd1, nd2): (f32, f32) = calc_nd1nd2(self)?;
 
-        // Calculation uses 365.25 for T: Time of days per year.
-        let option_type: T = T::from::<f64>(self.option_type.into()).unwrap();
-        let theta = (-(self.s * sigma * T::E().powf(-self.q * self.t) * nprimed1
-            / (T::from(2.).unwrap() * self.t.sqrt()))
-            - self.r * self.k * T::E().powf(-self.r * self.t) * nd2 * option_type
-            + self.q * self.s * T::E().powf(-self.q * self.t) * nd1 * option_type)
-            / T::from(DAYS_PER_YEAR).unwrap();
+        // Calculation uses 365.25 for f32: Time of days per year.
+        let option_type: f64 = self.option_type.into();
+        let theta = (-(self.s * sigma * E.powf(-self.q * self.t) * nprimed1
+            / (2.0 * self.t.sqrt()))
+            - self.r * self.k * E.powf(-self.r * self.t) * nd2 * option_type as f32
+            + self.q * self.s * E.powf(-self.q * self.t) * nd1 * option_type as f32)
+            / DAYS_PER_YEAR;
 
         Ok(theta)
     }
@@ -110,20 +105,16 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the vega of the option.
+    /// f32 of the vega of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let vega = inputs.calc_vega().unwrap();
     /// ```
-    fn calc_vega(&self) -> Result<T, String> {
-        let nprimed1: T = calc_nprimed1(self)?;
-        let vega: T = T::from(0.01).unwrap()
-            * self.s
-            * T::E().powf(-self.q * self.t)
-            * self.t.sqrt()
-            * nprimed1;
+    fn calc_vega(&self) -> Result<f32, String> {
+        let nprimed1: f32 = calc_nprimed1(self)?;
+        let vega: f32 = 0.01 * self.s * E.powf(-self.q * self.t) * self.t.sqrt() * nprimed1;
         Ok(vega)
     }
 
@@ -131,23 +122,18 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the rho of the option.
+    /// f32 of the rho of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let rho = inputs.calc_rho().unwrap();
     /// ```
-    fn calc_rho(&self) -> Result<T, String> {
-        let (_, nd2): (T, T) = calc_nd1nd2(self)?;
+    fn calc_rho(&self) -> Result<f32, String> {
+        let (_, nd2): (f32, f32) = calc_nd1nd2(self)?;
 
-        let option_type: T = T::from::<f64>(self.option_type.into()).unwrap();
-        let rho = option_type
-            * T::from(0.01).unwrap()
-            * self.k
-            * self.t
-            * T::E().powf(-self.r * self.t)
-            * nd2;
+        let option_type: f64 = self.option_type.into();
+        let rho = option_type as f32 / 100.0 * self.k * self.t * E.powf(-self.r * self.t) * nd2;
 
         Ok(rho)
     }
@@ -162,19 +148,19 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the epsilon of the option.
+    /// f32 of the epsilon of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let epsilon = inputs.calc_epsilon().unwrap();
     /// ```
-    fn calc_epsilon(&self) -> Result<T, String> {
+    fn calc_epsilon(&self) -> Result<f32, String> {
         let (nd1, _) = calc_nd1nd2(self)?;
-        let e_negqt = T::E().powf(-self.q * self.t);
+        let e_negqt = E.powf(-self.q * self.t);
 
-        let option_type: T = T::from::<f64>(self.option_type.into()).unwrap();
-        let epsilon: T = -self.s * self.t * e_negqt * nd1 * option_type;
+        let option_type: f64 = self.option_type.into();
+        let epsilon: f32 = -self.s * self.t * e_negqt * nd1 * option_type as f32;
 
         Ok(epsilon)
     }
@@ -183,14 +169,14 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the lambda of the option.
+    /// f32 of the lambda of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks, Pricing};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let lambda = inputs.calc_lambda().unwrap();
     /// ```
-    fn calc_lambda(&self) -> Result<T, String> {
+    fn calc_lambda(&self) -> Result<f32, String> {
         let delta = self.calc_delta()?;
         Ok(delta * self.s / self.calc_price()?)
     }
@@ -199,22 +185,21 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the vanna of the option.
+    /// f32 of the vanna of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let vanna = inputs.calc_vanna().unwrap();
     /// ```
-    fn calc_vanna(&self) -> Result<T, String> {
+    fn calc_vanna(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
 
         let nprimed1 = calc_nprimed1(self)?;
         let (_, d2) = calc_d1d2(self)?;
-        let vanna: T =
-            d2 * T::E().powf(-self.q * self.t) * nprimed1 * T::from(-0.01).unwrap() / sigma;
+        let vanna: f32 = d2 * E.powf(-self.q * self.t) * nprimed1 * -0.01 / sigma;
         Ok(vanna)
     }
 
@@ -222,28 +207,26 @@ where
     // /// # Requires
     // /// s, k, r, q, t, sigma
     // /// # Returns
-    // /// T of the charm of the option.
+    // /// f32 of the charm of the option.
     // /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let charm = inputs.calc_charm().unwrap();
     /// ```
-    fn calc_charm(&self) -> Result<T, String> {
+    fn calc_charm(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let nprimed1 = calc_nprimed1(self)?;
         let (nd1, _) = calc_nd1nd2(self)?;
         let (_, d2) = calc_d1d2(self)?;
-        let e_negqt = T::E().powf(-self.q * self.t);
+        let e_negqt = E.powf(-self.q * self.t);
 
-        let option_type: T = T::from::<f64>(self.option_type.into()).unwrap();
-        let charm: T = option_type * self.q * e_negqt * nd1
-            - e_negqt
-                * nprimed1
-                * (T::from(2.0).unwrap() * (self.r - self.q) * self.t - d2 * sigma * self.t.sqrt())
-                / (T::from(2.0).unwrap() * self.t * sigma * self.t.sqrt());
+        let option_type: f64 = self.option_type.into();
+        let charm: f32 = option_type as f32 * self.q * e_negqt * nd1
+            - e_negqt * nprimed1 * (2.0 * (self.r - self.q) * self.t - d2 * sigma * self.t.sqrt())
+                / (2.0 * self.t * sigma * self.t.sqrt());
 
         Ok(charm)
     }
@@ -252,27 +235,27 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the veta of the option.
+    /// f32 of the veta of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let veta = inputs.calc_veta().unwrap();
     /// ```
-    fn calc_veta(&self) -> Result<T, String> {
+    fn calc_veta(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let nprimed1 = calc_nprimed1(self)?;
         let (d1, d2) = calc_d1d2(self)?;
-        let e_negqt = T::E().powf(-self.q * self.t);
+        let e_negqt = E.powf(-self.q * self.t);
 
         let veta = -self.s
             * e_negqt
             * nprimed1
             * self.t.sqrt()
             * (self.q + ((self.r - self.q) * d1) / (sigma * self.t.sqrt())
-                - ((T::one() + d1 * d2) / (T::from(2.0).unwrap() * self.t)));
+                - ((1.0 + d1 * d2) / (2.0 * self.t)));
         Ok(veta)
     }
 
@@ -280,17 +263,17 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the vomma of the option.
+    /// f32 of the vomma of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let vomma = inputs.calc_vomma().unwrap();
     /// ```
-    fn calc_vomma(&self) -> Result<T, String> {
+    fn calc_vomma(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let (d1, d2) = calc_d1d2(self)?;
 
         let vomma = Inputs::calc_vega(self)? * ((d1 * d2) / sigma);
@@ -301,21 +284,21 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the speed of the option.
+    /// f32 of the speed of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let speed = inputs.calc_speed().unwrap();
     /// ```
-    fn calc_speed(&self) -> Result<T, String> {
+    fn calc_speed(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let (d1, _) = calc_d1d2(self)?;
         let gamma = Inputs::calc_gamma(self)?;
 
-        let speed = -gamma / self.s * (d1 / (sigma * self.t.sqrt()) + T::one());
+        let speed = -gamma / self.s * (d1 / (sigma * self.t.sqrt()) + 1.0);
         Ok(speed)
     }
 
@@ -323,21 +306,21 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the zomma of the option.
+    /// f32 of the zomma of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let zomma = inputs.calc_zomma().unwrap();
     /// ```
-    fn calc_zomma(&self) -> Result<T, String> {
+    fn calc_zomma(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let (d1, d2) = calc_d1d2(self)?;
         let gamma = Inputs::calc_gamma(self)?;
 
-        let zomma = gamma * ((d1 * d2 - T::one()) / sigma);
+        let zomma = gamma * ((d1 * d2 - 1.0) / sigma);
         Ok(zomma)
     }
 
@@ -345,27 +328,26 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the color of the option.
+    /// f32 of the color of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let color = inputs.calc_color().unwrap();
     /// ```
-    fn calc_color(&self) -> Result<T, String> {
+    fn calc_color(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let (d1, d2) = calc_d1d2(self)?;
         let nprimed1 = calc_nprimed1(self)?;
-        let e_negqt = T::E().powf(-self.q * self.t);
+        let e_negqt = E.powf(-self.q * self.t);
 
         let color = -e_negqt
-            * (nprimed1 / (T::from(2.0).unwrap() * self.s * self.t * sigma * self.t.sqrt()))
-            * (T::from(2.0).unwrap() * self.q * self.t
-                + T::one()
-                + (T::from(2.0).unwrap() * (self.r - self.q) * self.t
-                    - d2 * sigma * self.t.sqrt())
+            * (nprimed1 / (2.0 * self.s * self.t * sigma * self.t.sqrt()))
+            * (2.0 * self.q * self.t
+                + 1.0
+                + (2.0 * (self.r - self.q) * self.t - d2 * sigma * self.t.sqrt())
                     / (sigma * self.t.sqrt())
                     * d1);
         Ok(color)
@@ -375,24 +357,22 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the ultima of the option.
+    /// f32 of the ultima of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let ultima = inputs.calc_ultima().unwrap();
     /// ```
-    fn calc_ultima(&self) -> Result<T, String> {
+    fn calc_ultima(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let (d1, d2) = calc_d1d2(self)?;
         let vega = Inputs::calc_vega(self)?;
 
-        let ultima = -vega / sigma.powf(T::from(2.0).unwrap())
-            * (d1 * d2 * (T::one() - d1 * d2)
-                + d1.powf(T::from(2.0).unwrap())
-                + d2.powf(T::from(2.0).unwrap()));
+        let ultima =
+            -vega / sigma.powf(2.0) * (d1 * d2 * (1.0 - d1 * d2) + d1.powf(2.0) + d2.powf(2.0));
         Ok(ultima)
     }
 
@@ -400,16 +380,16 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the dual delta of the option.
+    /// f32 of the dual delta of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let dual_delta = inputs.calc_dual_delta().unwrap();
     /// ```
-    fn calc_dual_delta(&self) -> Result<T, String> {
+    fn calc_dual_delta(&self) -> Result<f32, String> {
         let (_, nd2) = calc_nd1nd2(self)?;
-        let e_negqt = T::E().powf(-self.q * self.t);
+        let e_negqt = E.powf(-self.q * self.t);
 
         let dual_delta = match self.option_type {
             OptionType::Call => -e_negqt * nd2,
@@ -422,19 +402,19 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// T of the dual gamma of the option.
+    /// f32 of the dual gamma of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let dual_gamma = inputs.calc_dual_gamma().unwrap();
     /// ```
-    fn calc_dual_gamma(&self) -> Result<T, String> {
+    fn calc_dual_gamma(&self) -> Result<f32, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
         let nprimed2 = calc_nprimed2(self)?;
-        let e_negqt = T::E().powf(-self.q * self.t);
+        let e_negqt = E.powf(-self.q * self.t);
 
         let dual_gamma = e_negqt * (nprimed2 / (self.k * sigma * self.t.sqrt()));
         Ok(dual_gamma)
@@ -444,15 +424,15 @@ where
     /// # Requires
     /// s, k, r, q, t, sigma
     /// # Returns
-    /// HashMap of type <String, T> of all Greeks of the option.
+    /// HashMap of type <String, f32> of all Greeks of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Greeks};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let greeks = inputs.calc_all_greeks().unwrap();
     /// ```
-    fn calc_all_greeks(&self) -> Result<HashMap<String, T>, String> {
-        let mut greeks: HashMap<String, T> = HashMap::with_capacity(17);
+    fn calc_all_greeks(&self) -> Result<HashMap<String, f32>, String> {
+        let mut greeks: HashMap<String, f32> = HashMap::with_capacity(17);
         greeks.insert("delta".into(), self.calc_delta()?);
         greeks.insert("gamma".into(), self.calc_gamma()?);
         greeks.insert("theta".into(), self.calc_theta()?);
diff --git a/src/implied_volatility.rs b/src/implied_volatility.rs
index 14bdbd6..18e5cf0 100644
--- a/src/implied_volatility.rs
+++ b/src/implied_volatility.rs
@@ -1,22 +1,17 @@
-use num_traits::{float::FloatConst, AsPrimitive, Float, FromPrimitive};
+use num_traits::Float;
 
-use crate::{
-    greeks::Greeks, lets_be_rational::implied_volatility_from_a_transformed_rational_guess,
-    pricing::Pricing, Inputs, A, B, C, D, F, SQRT_2PI, _E,
-};
+use crate::lets_be_rational::implied_volatility_from_a_transformed_rational_guess;
+use crate::{greeks::Greeks, pricing::Pricing, Inputs, *};
 
 pub trait ImpliedVolatility<T>: Pricing<T> + Greeks<T>
 where
     T: Float,
 {
     fn calc_iv(&self, tolerance: T) -> Result<T, String>;
-    fn calc_rational_iv(&self) -> Result<T, String>;
+    fn calc_rational_iv(&self) -> Result<f64, String>;
 }
 
-impl<T> ImpliedVolatility<T> for Inputs<T>
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
+impl ImpliedVolatility<f32> for Inputs {
     /// Calculates the implied volatility of the option.
     /// Tolerance is the max error allowed for the implied volatility,
     /// the lower the tolerance the more iterations will be required.
@@ -26,7 +21,7 @@ where
     /// # Requires
     /// s, k, r, q, t, p
     /// # Returns
-    /// T of the implied volatility of the option.
+    /// f32 of the implied volatility of the option.
     /// # Example:
     /// ```
     /// use blackscholes::{Inputs, OptionType, ImpliedVolatility};
@@ -37,41 +32,41 @@ where
     /// A more accurate method is the "Let's be rational" method from ["Let’s be rational" (2016) by Peter Jackel](http://www.jaeckel.org/LetsBeRational.pdf)
     /// however this method is much more complicated, it is available as calc_rational_iv().
     #[allow(non_snake_case)]
-    fn calc_iv(&self, tolerance: T) -> Result<T, String> {
-        let mut inputs: Inputs<T> = self.clone();
+    fn calc_iv(&self, tolerance: f32) -> Result<f32, String> {
+        let mut inputs: Inputs = self.clone();
 
         let p = self
             .p
-            .ok_or("inputs.p must contain Some(T), found None".to_string())?;
-
-        let half = T::from(0.5).unwrap();
-
-        let X: T = inputs.k * T::E().powf(-inputs.r * inputs.t);
-        let fminusX: T = inputs.s - X;
-        let fplusX: T = inputs.s + X;
-        let oneoversqrtT: T = T::one() / inputs.t.sqrt();
-
-        let x: T = oneoversqrtT * (T::from(SQRT_2PI).unwrap() / (fplusX));
-        let y: T = p - (inputs.s - inputs.k) * half
-            + ((p - fminusX * half).powi(2) - fminusX.powi(2) / T::PI()).sqrt();
-
-        let mut sigma: T = oneoversqrtT
-            * (T::from(SQRT_2PI).unwrap() / fplusX)
-            * (p - fminusX * half
-                + ((p - fminusX * half).powi(2) - fminusX.powi(2) / T::PI()).sqrt())
-            + T::from(A).unwrap()
-            + T::from(B).unwrap() / x
-            + T::from(C).unwrap() * y
-            + T::from(D).unwrap() / x.powi(2)
-            + T::from(_E).unwrap() * y.powi(2)
-            + T::from(F).unwrap() * y / x;
+            .ok_or("inputs.p must contain Some(f32), found None".to_string())?;
+        // Initialize estimation of sigma using Brenn and Subrahmanyam (1998) method of calculating initial iv estimation.
+        // commented out to replace with modified corrado-miller method.
+        // let mut sigma: f32 = (PI2 / inputs.t).sqrt() * (p / inputs.s);
+
+        let X: f32 = inputs.k * E.powf(-inputs.r * inputs.t);
+        let fminusX: f32 = inputs.s - X;
+        let fplusX: f32 = inputs.s + X;
+        let oneoversqrtT: f32 = 1.0 / inputs.t.sqrt();
+
+        let x: f32 = oneoversqrtT * (SQRT_2PI / (fplusX));
+        let y: f32 = p - (inputs.s - inputs.k) / 2.0
+            + ((p - fminusX / 2.0).powf(2.0) - fminusX.powf(2.0) / PI).sqrt();
+
+        let mut sigma: f32 = oneoversqrtT
+            * (SQRT_2PI / fplusX)
+            * (p - fminusX / 2.0 + ((p - fminusX / 2.0).powf(2.0) - fminusX.powf(2.0) / PI).sqrt())
+            + A
+            + B / x
+            + C * y
+            + D / x.powf(2.0)
+            + _E * y.powf(2.0)
+            + F * y / x;
 
         if sigma.is_nan() {
             Err("Failed to converge".to_string())?
         }
 
         // Initialize diff to 100 for use in while loop
-        let mut diff: T = T::from(100.0).unwrap();
+        let mut diff: f32 = 100.0;
 
         // Uses Newton Raphson algorithm to calculate implied volatility.
         // Test if the difference between calculated option price and actual option price is > tolerance,
@@ -79,7 +74,7 @@ where
         while diff.abs() > tolerance {
             inputs.sigma = Some(sigma);
             diff = Inputs::calc_price(&inputs)? - p;
-            sigma = sigma - diff / (Inputs::calc_vega(&inputs)? * T::from(100.0).unwrap());
+            sigma -= diff / (Inputs::calc_vega(&inputs)? * 100.0);
 
             if sigma.is_nan() || sigma.is_infinite() {
                 Err("Failed to converge".to_string())?
@@ -103,7 +98,7 @@ where
     /// Uses the "Let's be rational" method from ["Let’s be rational" (2016) by Peter Jackel](http://www.jaeckel.org/LetsBeRational.pdf)
     /// from Jackel's C++ implementation, imported through the C FFI.  The C++ implementation is available at [here](http://www.jaeckel.org/LetsBeRational.7z)
     /// Per Jackel's whitepaper, this method can solve for the implied volatility to f64 precision in 2 iterations.
-    fn calc_rational_iv(&self) -> Result<T, String> {
+    fn calc_rational_iv(&self) -> Result<f64, String> {
         // extract price, or return error
         let p = self.p.ok_or("Option price is required".to_string())?;
 
@@ -117,14 +112,14 @@ where
         let f = f * (-self.q * self.t).exp();
 
         let sigma = implied_volatility_from_a_transformed_rational_guess(
-            p,
-            f,
-            self.k,
-            self.t,
+            p as f64,
+            f as f64,
+            self.k as f64,
+            self.t as f64,
             self.option_type,
         );
 
-        if sigma.is_nan() || sigma.is_infinite() || sigma < T::zero() {
+        if sigma.is_nan() || sigma.is_infinite() || sigma < 0.0 {
             Err("Implied volatility failed to converge".to_string())?
         }
         Ok(sigma)
diff --git a/src/inputs.rs b/src/inputs.rs
index ae83919..60aac73 100644
--- a/src/inputs.rs
+++ b/src/inputs.rs
@@ -37,27 +37,27 @@ impl From<OptionType> for f64 {
 
 /// The inputs to the Black-Scholes-Merton model.
 #[derive(Debug, Clone, PartialEq)]
-pub struct Inputs<T> {
+pub struct Inputs {
     /// The type of the option (call or put)
     pub option_type: OptionType,
     /// Stock price
-    pub s: T,
+    pub s: f32,
     /// Strike price
-    pub k: T,
+    pub k: f32,
     /// Option price
-    pub p: Option<T>,
+    pub p: Option<f32>,
     /// Risk-free rate
-    pub r: T,
+    pub r: f32,
     /// Dividend yield
-    pub q: T,
+    pub q: f32,
     /// Time to maturity in years
-    pub t: T,
+    pub t: f32,
     /// Volatility
-    pub sigma: Option<T>,
+    pub sigma: Option<f32>,
 }
 
 /// Methods for calculating the price, greeks, and implied volatility of an option.
-impl<T> Inputs<T> {
+impl Inputs {
     /// Creates instance ot the `Inputs` struct.
     /// # Arguments
     /// * `option_type` - The type of option to be priced.
@@ -78,13 +78,13 @@ impl<T> Inputs<T> {
     #[allow(clippy::too_many_arguments)]
     pub fn new(
         option_type: OptionType,
-        s: T,
-        k: T,
-        p: Option<T>,
-        r: T,
-        q: T,
-        t: T,
-        sigma: Option<T>,
+        s: f32,
+        k: f32,
+        p: Option<f32>,
+        r: f32,
+        q: f32,
+        t: f32,
+        sigma: Option<f32>,
     ) -> Self {
         Self {
             option_type,
@@ -99,10 +99,7 @@ impl<T> Inputs<T> {
     }
 }
 
-impl<T> Display for Inputs<T>
-where
-    T: std::fmt::Display + Copy,
-{
+impl Display for Inputs {
     fn fmt(&self, f: &mut Formatter) -> fmtResult {
         writeln!(f, "Option type: {}", self.option_type)?;
         writeln!(f, "Stock price: {:.2}", self.s)?;
diff --git a/src/lets_be_rational/black.rs b/src/lets_be_rational/black.rs
index 1c204a2..abdab58 100644
--- a/src/lets_be_rational/black.rs
+++ b/src/lets_be_rational/black.rs
@@ -1,4 +1,3 @@
-use num_traits::{AsPrimitive, Float, FromPrimitive};
 use std::f64::consts::FRAC_1_SQRT_2;
 
 use statrs::function::erf::erfc;
@@ -15,14 +14,10 @@ const CODYS_THRESHOLD: f64 = 0.46875;
 
 const SMALL_T_EXPANSION_OF_NORMALISED_BLACK_THRESHOLD: f64 = 2.0 * SIXTEENTH_ROOT_DBL_EPSILON;
 
-fn erfcx<T>(x: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    (x * x).exp() * T::from(erfc(x.as_())).unwrap()
+fn erfcx(x: f64) -> f64 {
+    (x * x).exp() * erfc(x)
 }
 
-#[allow(dead_code)]
 fn normalised_black_call_using_norm_cdf(x: f64, s: f64) -> f64 {
     let h = x / s;
     let t = 0.5 * s;
@@ -31,56 +26,45 @@ fn normalised_black_call_using_norm_cdf(x: f64, s: f64) -> f64 {
     b.abs().max(0.0)
 }
 
-fn normalised_black_call_with_optimal_use_of_codys_functions<T>(x: T, s: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
+fn normalised_black_call_with_optimal_use_of_codys_functions(x: f64, s: f64) -> f64 {
     let h = x / s;
-    let half = T::from(0.5).unwrap();
-    let t = half * s;
-    let q1 = -T::from(FRAC_1_SQRT_2).unwrap() * (h + t);
-    let q2 = -T::from(FRAC_1_SQRT_2).unwrap() * (h - t);
+    let t = 0.5 * s;
+    let q1 = -FRAC_1_SQRT_2 * (h + t);
+    let q2 = -FRAC_1_SQRT_2 * (h - t);
     let two_b;
 
-    if q1 < T::from(CODYS_THRESHOLD).unwrap() {
-        if q2 < T::from(CODYS_THRESHOLD).unwrap() {
-            two_b = (half * x).exp() * T::from(erfc(q1.as_())).unwrap()
-                - (-half * x).exp() * T::from(erfc(q2.as_())).unwrap();
+    if q1 < CODYS_THRESHOLD {
+        if q2 < CODYS_THRESHOLD {
+            two_b = (0.5 * x).exp() * erfc(q1) - (-0.5 * x).exp() * erfc(q2);
         } else {
-            two_b = (half * x).exp() * T::from(erfc(q1.as_())).unwrap()
-                - (-half * (h * h + t * t)).exp() * erfcx(q2);
+            two_b = (0.5 * x).exp() * erfc(q1) - (-0.5 * (h * h + t * t)).exp() * erfcx(q2);
         }
-    } else if q2 < T::from(CODYS_THRESHOLD).unwrap() {
-        two_b = (-half * (h * h + t * t)).exp() * erfcx(q1)
-            - (-half * x).exp() * T::from(erfc(q2.as_())).unwrap();
+    } else if q2 < CODYS_THRESHOLD {
+        two_b = (-0.5 * (h * h + t * t)).exp() * erfcx(q1) - (-0.5 * x).exp() * erfc(q2);
     } else {
-        two_b = (-half * (h * h + t * t)).exp() * (erfcx(q1) - erfcx(q2));
+        two_b = (-0.5 * (h * h + t * t)).exp() * (erfcx(q1) - erfcx(q2));
     }
 
-    (half * two_b).abs().max(T::from(0.0).unwrap())
+    (0.5 * two_b).abs().max(0.0)
 }
 
 #[rustfmt::skip]
-fn small_t_expansion_of_normalised_black_call<T>(h: T, t: T) -> T 
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    let a = T::one() + h * (T::from(0.5).unwrap() * T::from(SQRT_TWO_PI).unwrap()) * erfcx(-T::from(FRAC_1_SQRT_2).unwrap() * h);
+fn small_t_expansion_of_normalised_black_call(h: f64, t: f64) -> f64 {
+    let a = 1.0 + h * (0.5 * SQRT_TWO_PI) * erfcx(-FRAC_1_SQRT_2 * h);
     let w = t * t;
     let h2 = h * h;
 
-    let expansion = T::from(2.0).unwrap() * t * (a + w * ((-T::from(1.0).unwrap() + T::from(3.0).unwrap() * a + a * h2) / T::from(6.0).unwrap()
-        + w * ((-T::from(7.0).unwrap() + T::from(15.0).unwrap() * a + h2 * (-T::from(1.0).unwrap() + T::from(10.0).unwrap() * a + a * h2)) / T::from(120.0).unwrap()
-        + w * ((-T::from(57.0).unwrap() + T::from(105.0).unwrap() * a + h2 * (-T::from(18.0).unwrap() + T::from(105.0).unwrap() * a + h2 * (-T::from(1.0).unwrap() + T::from(21.0).unwrap() * a + a * h2))) / T::from(5040.0).unwrap()
-        + w * ((-T::from(561.0).unwrap() + T::from(945.0).unwrap() * a + h2 * (-T::from(285.0).unwrap() + T::from(1260.0).unwrap() * a + h2 * (-T::from(33.0).unwrap() + T::from(378.0).unwrap() * a + h2 * (-T::from(1.0).unwrap() + T::from(36.0).unwrap() * a + a * h2)))) / T::from(362880.0).unwrap()
-        + w * ((-T::from(6555.0).unwrap() + T::from(10395.0).unwrap() * a + h2 * (-T::from(4680.0).unwrap() + T::from(17325.0).unwrap() * a + h2 * (-T::from(840.0).unwrap() + T::from(6930.0).unwrap() * a + h2 * (-T::from(52.0).unwrap() + T::from(990.0).unwrap() * a + h2 * (-T::from(1.0).unwrap() + T::from(55.0).unwrap() * a + a * h2))))) / T::from(39916800.0).unwrap()
-        + w * ((-T::from(89055.0).unwrap() + T::from(135135.0).unwrap() * a + h2 * (-T::from(82845.0).unwrap() + T::from(270270.0).unwrap() * a + h2 * (-T::from(20370.0).unwrap() + T::from(135135.0).unwrap() * a + h2 * (-T::from(1926.0).unwrap() + T::from(25740.0).unwrap() * a + h2 * (-T::from(75.0).unwrap() + T::from(2145.0).unwrap() * a + h2 * (-T::from(1.0).unwrap() + T::from(78.0).unwrap() * a + a * h2)))))) / T::from(6227020800.0).unwrap())))))));
+    let expansion = 2.0 * t * (a + w * ((-1.0 + 3.0 * a + a * h2) / 6.0
+        + w * ((-7.0 + 15.0 * a + h2 * (-1.0 + 10.0 * a + a * h2)) / 120.0
+        + w * ((-57.0 + 105.0 * a + h2 * (-18.0 + 105.0 * a + h2 * (-1.0 + 21.0 * a + a * h2))) / 5040.0
+        + w * ((-561.0 + 945.0 * a + h2 * (-285.0 + 1260.0 * a + h2 * (-33.0 + 378.0 * a + h2 * (-1.0 + 36.0 * a + a * h2)))) / 362880.0
+        + w * ((-6555.0 + 10395.0 * a + h2 * (-4680.0 + 17325.0 * a + h2 * (-840.0 + 6930.0 * a + h2 * (-52.0 + 990.0 * a + h2 * (-1.0 + 55.0 * a + a * h2))))) / 39916800.0
+        + w * ((-89055.0 + 135135.0 * a + h2 * (-82845.0 + 270270.0 * a + h2 * (-20370.0 + 135135.0 * a + h2 * (-1926.0 + 25740.0 * a + h2 * (-75.0 + 2145.0 * a + h2 * (-1.0 + 78.0 * a + a * h2)))))) / 6227020800.0)))))));
 
-    let b = T::from(ONE_OVER_SQRT_TWO_PI).unwrap() * (-T::from(0.5).unwrap() * (h * h + t * t)).exp() * expansion;
-    b.abs().max(T::zero())
+    let b = ONE_OVER_SQRT_TWO_PI * (-0.5 * (h * h + t * t)).exp() * expansion;
+    b.abs().max(0.0)
 }
 
-#[allow(dead_code)]
 pub fn normalised_black_call_using_erfcx(h: f64, t: f64) -> f64 {
     let b = 0.5
         * (-0.5 * (h * h + t * t)).exp()
@@ -88,69 +72,59 @@ pub fn normalised_black_call_using_erfcx(h: f64, t: f64) -> f64 {
     b.abs().max(0.0)
 }
 
-pub(crate) fn normalised_black_call<T>(x: T, s: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    if x > T::zero() {
+pub(crate) fn normalised_black_call(x: f64, s: f64) -> f64 {
+    if x > 0.0 {
         return normalised_intrinsic(x, OptionType::Call) + normalised_black_call(-x, s);
         // In the money.
     }
-    if s <= x.abs() * T::from(DENORMALISATION_CUTOFF).unwrap() {
+    if s <= x.abs() * DENORMALISATION_CUTOFF {
         return normalised_intrinsic(x, OptionType::Call); // sigma=0 -> intrinsic value.
     }
 
     // Denote h := x/s and t := s/2.
     // We evaluate the condition |h|>|η|, i.e., h<η  &&  t < τ+|h|-|η|  avoiding any divisions by s , where η = asymptotic_expansion_accuracy_threshold  and τ = small_t_expansion_of_normalised_black_threshold .
-    if x < s * T::from(ASYMPTOTIC_EXPANSION_ACCURACY_THRESHOLD).unwrap()
-        && T::from(0.5).unwrap() * s * s + x
-            < s * (T::from(SMALL_T_EXPANSION_OF_NORMALISED_BLACK_THRESHOLD).unwrap()
-                + T::from(ASYMPTOTIC_EXPANSION_ACCURACY_THRESHOLD).unwrap())
+    if x < s * ASYMPTOTIC_EXPANSION_ACCURACY_THRESHOLD
+        && 0.5 * s * s + x
+            < s * (SMALL_T_EXPANSION_OF_NORMALISED_BLACK_THRESHOLD
+                + ASYMPTOTIC_EXPANSION_ACCURACY_THRESHOLD)
     {
-        return asymptotic_expansion_of_normalised_black_call(x / s, T::from(0.5).unwrap() * s);
+        return asymptotic_expansion_of_normalised_black_call(x / s, 0.5 * s);
     }
-    if T::from(0.5).unwrap() * s < T::from(SMALL_T_EXPANSION_OF_NORMALISED_BLACK_THRESHOLD).unwrap()
-    {
-        return small_t_expansion_of_normalised_black_call(x / s, T::from(0.5).unwrap() * s);
+    if 0.5 * s < SMALL_T_EXPANSION_OF_NORMALISED_BLACK_THRESHOLD {
+        return small_t_expansion_of_normalised_black_call(x / s, 0.5 * s);
     }
 
     normalised_black_call_with_optimal_use_of_codys_functions(x, s)
 }
 
-pub(crate) fn normalised_black<T>(x: T, s: T, q: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    normalised_black_call(if q < T::zero() { -x } else { x }, s) /* Reciprocal-strike call-put equivalence */
+pub(crate) fn normalised_black(x: f64, s: f64, q: f64) -> f64 {
+    normalised_black_call(if q < 0.0 { -x } else { x }, s) /* Reciprocal-strike call-put equivalence */
 }
 
 #[rustfmt::skip]
-pub fn asymptotic_expansion_of_normalised_black_call<T>(h: T, t: T) -> T 
-where
-    T: Float + FromPrimitive,
-{
+pub fn asymptotic_expansion_of_normalised_black_call(h: f64, t: f64) -> f64 {
     let e = (t / h) * (t / h);
     let r = (h + t) * (h - t);
     let q = (h / r) * (h / r);
 
     // 17th order asymptotic expansion of A(h,t) in q
-    let asymptotic_expansion_sum = T::from(2.0).unwrap() + q * (-T::from(6.0).unwrap() - T::from(2.0).unwrap() * e + T::from(3.0).unwrap() * q * (T::from(10.0).unwrap() + e * (T::from(20.0).unwrap() + T::from(2.0).unwrap() * e)
-        + T::from(5.0).unwrap() * q * (-T::from(14.0).unwrap() + e * (-T::from(70.0).unwrap() + e * (-T::from(42.0).unwrap() - T::from(2.0).unwrap() * e))
-        + T::from(7.0).unwrap() * q * (T::from(18.0).unwrap() + e * (T::from(168.0).unwrap() + e * (T::from(252.0).unwrap() + e * (T::from(72.0).unwrap() + T::from(2.0).unwrap() * e)))
-        + T::from(9.0).unwrap() * q * (-T::from(22.0).unwrap() + e * (-T::from(330.0).unwrap() + e * (-T::from(924.0).unwrap() + e * (-T::from(660.0).unwrap() + e * (-T::from(110.0).unwrap() - T::from(2.0).unwrap() * e))))
-        + T::from(11.0).unwrap() * q * (T::from(26.0).unwrap() + e * (T::from(572.0).unwrap() + e * (T::from(2574.0).unwrap() + e * (T::from(3432.0).unwrap() + e * (T::from(1430.0).unwrap() + e * (T::from(156.0).unwrap() + T::from(2.0).unwrap() * e)))))
-        + T::from(13.0).unwrap() * q * (-T::from(30.0).unwrap() + e * (-T::from(910.0).unwrap() + e * (-T::from(6006.0).unwrap() + e * (-T::from(12870.0).unwrap() + e * (-T::from(10010.0).unwrap() + e * (-T::from(2730.0).unwrap() + e * (-T::from(210.0).unwrap() - T::from(2.0).unwrap() * e)))))
-        + T::from(15.0).unwrap() * q * (T::from(34.0).unwrap() + e * (T::from(1360.0).unwrap() + e * (T::from(12376.0).unwrap() + e * (T::from(38896.0).unwrap() + e * (T::from(48620.0).unwrap() + e * (T::from(24752.0).unwrap() + e * (T::from(4760.0).unwrap() + e * (T::from(272.0).unwrap() + T::from(2.0).unwrap() * e)))))))
-        + T::from(17.0).unwrap() * q * (-T::from(38.0).unwrap() + e * (-T::from(1938.0).unwrap() + e * (-T::from(23256.0).unwrap() + e * (-T::from(100776.0).unwrap() + e * (-T::from(184756.0).unwrap() + e * (-T::from(151164.0).unwrap() + e * (-T::from(54264.0).unwrap() + e * (-T::from(7752.0).unwrap() + e * (-T::from(342.0).unwrap() - T::from(2.0).unwrap() * e))))))))
-        + T::from(19.0).unwrap() * q * (T::from(42.0).unwrap() + e * (T::from(2660.0).unwrap() + e * (T::from(40698.0).unwrap() + e * (T::from(232560.0).unwrap() + e * (T::from(587860.0).unwrap() + e * (T::from(705432.0).unwrap() + e * (T::from(406980.0).unwrap() + e * (T::from(108528.0).unwrap() + e * (T::from(11970.0).unwrap() + e * (T::from(420.0).unwrap() + T::from(2.0).unwrap() * e)))))))))
-        + T::from(21.0).unwrap() * q * (-T::from(46.0).unwrap() + e * (-T::from(3542.0).unwrap() + e * (-T::from(67298.0).unwrap() + e * (-T::from(490314.0).unwrap() + e * (-T::from(1634380.0).unwrap() + e * (-T::from(2704156.0).unwrap() + e * (-T::from(2288132.0).unwrap() + e * (-T::from(980628.0).unwrap() + e * (-T::from(201894.0).unwrap() + e * (-T::from(17710.0).unwrap() + e * (-T::from(506.0).unwrap() - T::from(2.0).unwrap() * e))))))))))
-        + T::from(23.0).unwrap() * q * (T::from(50.0).unwrap() + e * (T::from(4600.0).unwrap() + e * (T::from(106260.0).unwrap() + e * (T::from(961400.0).unwrap() + e * (T::from(4085950.0).unwrap() + e * (T::from(8914800.0).unwrap() + e * (T::from(10400600.0).unwrap() + e * (T::from(6537520.0).unwrap() + e * (T::from(2163150.0).unwrap() + e * (T::from(354200.0).unwrap() + e * (T::from(25300.0).unwrap() + e * (T::from(600.0).unwrap() + T::from(2.0).unwrap() * e)))))))))))
-        + T::from(25.0).unwrap() * q * (-T::from(54.0).unwrap() + e * (-T::from(5850.0).unwrap() + e * (-T::from(161460.0).unwrap() + e * (-T::from(1776060.0).unwrap() + e * (-T::from(9373650.0).unwrap() + e * (-T::from(26075790.0).unwrap() + e * (-T::from(40116600.0).unwrap() + e * (-T::from(34767720.0).unwrap() + e * (-T::from(16872570.0).unwrap() + e * (-T::from(4440150.0).unwrap() + e * (-T::from(592020.0).unwrap() + e * (-T::from(35100.0).unwrap() + e * (-T::from(702.0).unwrap() - T::from(2.0).unwrap() * e))))))))))))
-        + T::from(27.0).unwrap() * q * (T::from(58.0).unwrap() + e * (T::from(7308.0).unwrap() + e * (T::from(237510.0).unwrap() + e * (T::from(3121560.0).unwrap() + e * (T::from(20030010.0).unwrap() + e * (T::from(69194580.0).unwrap() + e * (T::from(135727830.0).unwrap() + e * (T::from(155117520.0).unwrap() + e * (T::from(103791870.0).unwrap() + e * (T::from(40060020.0).unwrap() + e * (T::from(8584290.0).unwrap() + e * (T::from(950040.0).unwrap() + e * (T::from(47502.0).unwrap() + e * (T::from(812.0).unwrap() + T::from(2.0).unwrap() * e)))))))))))))
-        + T::from(29.0).unwrap() * q * (-T::from(62.0).unwrap() + e * (-T::from(8990.0).unwrap() + e * (-T::from(339822.0).unwrap() + e * (-T::from(5259150.0).unwrap() + e * (-T::from(40320150.0).unwrap() + e * (-T::from(169344630.0).unwrap() + e * (-T::from(412506150.0).unwrap() + e * (-T::from(601080390.0).unwrap() + e * (-T::from(530365050.0).unwrap() + e * (-T::from(282241050.0).unwrap() + e * (-T::from(88704330.0).unwrap() + e * (-T::from(15777450.0).unwrap() + e * (-T::from(14725620.0).unwrap() + e * (-T::from(629300.0).unwrap() + e * (-T::from(930.0).unwrap() - T::from(2.0).unwrap() * e)))))))))))))))
-        + T::from(31.0).unwrap() * q * (T::from(66.0).unwrap() + e * (T::from(109120.0).unwrap() + e * (T::from(474672.0).unwrap() + e * (T::from(8544096.0).unwrap() + e * (T::from(77134200.0).unwrap() + e * (T::from(387073440.0).unwrap() + e * (T::from(1146332880.0).unwrap() + e * (T::from(2074316640.0).unwrap() + e * (T::from(2333606220.0).unwrap() + e * (T::from(1637618400.0).unwrap() + e * (T::from(709634640.0).unwrap() + e * (T::from(185122080.0).unwrap() + e * (T::from(27768312.0).unwrap() + e * (T::from(2215136.0).unwrap() + e * (T::from(81840.0).unwrap() + e * (T::from(1056.0).unwrap() + T::from(2.0).unwrap() * e)))))))))))))))
-        + T::from(33.0).unwrap() * (-T::from(70.0).unwrap() + e * (-T::from(130900.0).unwrap() + e * (-T::from(649264.0).unwrap() + e * (-T::from(13449040.0).unwrap() + e * (-T::from(141214920.0).unwrap() + e * (-T::from(834451800.0).unwrap() + e * (-T::from(2952675600.0).unwrap() + e * (-T::from(6495886320.0).unwrap() + e * (-T::from(9075135300.0).unwrap() + e * (-T::from(8119857900.0).unwrap() + e * (-T::from(4639918800.0).unwrap() + e * (-T::from(1668903600.0).unwrap() + e * (-T::from(367158792.0).unwrap() + e * (-T::from(47071640.0).unwrap() + e * (-T::from(3246320.0).unwrap() + e * (-T::from(104720.0).unwrap() + e * (-T::from(1190.0).unwrap() - T::from(2.0).unwrap() * e))))))))))))))))) * q))))))))))))))));
-
-    let b = T::from(ONE_OVER_SQRT_TWO_PI).unwrap() * ((T::from(-0.5).unwrap() * (h * h + t * t)).exp()) * (t / r) * asymptotic_expansion_sum;
-    b.abs().max(T::zero())
+    let asymptotic_expansion_sum = 2.0 + q * (-6.0 - 2.0 * e + 3.0 * q * (10.0 + e * (20.0 + 2.0 * e)
+        + 5.0 * q * (-14.0 + e * (-70.0 + e * (-42.0 - 2.0 * e))
+        + 7.0 * q * (18.0 + e * (168.0 + e * (252.0 + e * (72.0 + 2.0 * e)))
+        + 9.0 * q * (-22.0 + e * (-330.0 + e * (-924.0 + e * (-660.0 + e * (-110.0 - 2.0 * e))))
+        + 11.0 * q * (26.0 + e * (572.0 + e * (2574.0 + e * (3432.0 + e * (1430.0 + e * (156.0 + 2.0 * e)))))
+        + 13.0 * q * (-30.0 + e * (-910.0 + e * (-6006.0 + e * (-12870.0 + e * (-10010.0 + e * (-2730.0 + e * (-210.0 - 2.0 * e)))))
+        + 15.0 * q * (34.0 + e * (1360.0 + e * (12376.0 + e * (38896.0 + e * (48620.0 + e * (24752.0 + e * (4760.0 + e * (272.0 + 2.0 * e)))))))
+        + 17.0 * q * (-38.0 + e * (-1938.0 + e * (-23256.0 + e * (-100776.0 + e * (-184756.0 + e * (-151164.0 + e * (-54264.0 + e * (-7752.0 + e * (-342.0 - 2.0 * e))))))))
+        + 19.0 * q * (42.0 + e * (2660.0 + e * (40698.0 + e * (232560.0 + e * (587860.0 + e * (705432.0 + e * (406980.0 + e * (108528.0 + e * (11970.0 + e * (420.0 + 2.0 * e)))))))))
+        + 21.0 * q * (-46.0 + e * (-3542.0 + e * (-67298.0 + e * (-490314.0 + e * (-1634380.0 + e * (-2704156.0 + e * (-2288132.0 + e * (-980628.0 + e * (-201894.0 + e * (-17710.0 + e * (-506.0 - 2.0 * e))))))))))
+        + 23.0 * q * (50.0 + e * (4600.0 + e * (106260.0 + e * (961400.0 + e * (4085950.0 + e * (8914800.0 + e * (10400600.0 + e * (6537520.0 + e * (2163150.0 + e * (354200.0 + e * (25300.0 + e * (600.0 + 2.0 * e)))))))))))
+        + 25.0 * q * (-54.0 + e * (-5850.0 + e * (-161460.0 + e * (-1776060.0 + e * (-9373650.0 + e * (-26075790.0 + e * (-40116600.0 + e * (-34767720.0 + e * (-16872570.0 + e * (-4440150.0 + e * (-592020.0 + e * (-35100.0 + e * (-702.0 - 2.0 * e))))))))))))
+        + 27.0 * q * (58.0 + e * (7308.0 + e * (237510.0 + e * (3121560.0 + e * (20030010.0 + e * (69194580.0 + e * (135727830.0 + e * (155117520.0 + e * (103791870.0 + e * (40060020.0 + e * (8584290.0 + e * (950040.0 + e * (47502.0 + e * (812.0 + 2.0 * e)))))))))))))
+        + 29.0 * q * (-62.0 + e * (-8990.0 + e * (-339822.0 + e * (-5259150.0 + e * (-40320150.0 + e * (-169344630.0 + e * (-412506150.0 + e * (-601080390.0 + e * (-530365050.0 + e * (-282241050.0 + e * (-88704330.0 + e * (-15777450.0 + e * (-14725620.0 + e * (-629300.0 + e * (-930.0 - 2.0 * e)))))))))))))))
+        + 31.0 * q * (66.0 + e * (109120.0 + e * (474672.0 + e * (8544096.0 + e * (77134200.0 + e * (387073440.0 + e * (1146332880.0 + e * (2074316640.0 + e * (2333606220.0 + e * (1637618400.0 + e * (709634640.0 + e * (185122080.0 + e * (27768312.0 + e * (2215136.0 + e * (81840.0 + e * (1056.0 + 2.0 * e)))))))))))))))
+        + 33.0 * (-70.0 + e * (-130900.0 + e * (-649264.0 + e * (-13449040.0 + e * (-141214920.0 + e * (-834451800.0 + e * (-2952675600.0 + e * (-6495886320.0 + e * (-9075135300.0 + e * (-8119857900.0 + e * (-4639918800.0 + e * (-1668903600.0 + e * (-367158792.0 + e * (-47071640.0 + e * (-3246320.0 + e * (-104720.0 + e * (-1190.0 - 2.0 * e))))))))))))))))) * q))))))))))))))));
+
+    let b = ONE_OVER_SQRT_TWO_PI * ((-0.5 * (h * h + t * t)).exp()) * (t / r) * asymptotic_expansion_sum;
+    b.abs().max(0.0)
 }
diff --git a/src/lets_be_rational/intrinsic.rs b/src/lets_be_rational/intrinsic.rs
index 06fa47d..2898668 100644
--- a/src/lets_be_rational/intrinsic.rs
+++ b/src/lets_be_rational/intrinsic.rs
@@ -1,34 +1,28 @@
 use crate::OptionType;
-use num_traits::{Float, FromPrimitive};
 
 const FOURTH_ROOT_DBL_EPSILON: f64 = 0.0001220703125;
 
 const NORMALISED_X2_THRESHOLD: f64 = 98.0 * FOURTH_ROOT_DBL_EPSILON;
 
-pub(crate) fn normalised_intrinsic<T>(x: T, option_type: OptionType) -> T
-where
-    T: Float + FromPrimitive,
-{
-    let q = T::from(option_type as i32).unwrap();
-    if q * x <= T::zero() {
-        return T::zero();
+pub(crate) fn normalised_intrinsic(x: f64, option_type: OptionType) -> f64 {
+    let q = option_type as i32 as f64;
+    if q * x <= 0.0 {
+        return 0.0;
     }
     let x2 = x * x;
-    if x2 < T::from(NORMALISED_X2_THRESHOLD).unwrap() {
-        return ((if q < T::zero() { -T::one() } else { T::one() })
+    if x2 < NORMALISED_X2_THRESHOLD {
+        return ((if q < 0.0 { -1.0 } else { 1.0 })
             * x
-            * (T::one()
-                + x2 * ((T::from(1.0 / 24.0).unwrap())
-                    + x2 * ((T::from(1.0 / 1920.0).unwrap())
-                        + x2 * ((T::from(1.0 / 322560.0).unwrap())
-                            + (T::from(1.0 / 92897280.0).unwrap()) * x2)))))
-            .max(T::zero())
+            * (1.0
+                + x2 * ((1.0 / 24.0)
+                    + x2 * ((1.0 / 1920.0) + x2 * ((1.0 / 322560.0) + (1.0 / 92897280.0) * x2)))))
+            .max(0.0)
             .abs();
     }
-    let b_max = (T::from(0.5).unwrap() * x).exp();
-    let one_over_b_max = T::one() / b_max;
-    ((if q < T::zero() { -T::one() } else { T::one() }) * (b_max - one_over_b_max))
-        .max(T::zero())
+    let b_max = (0.5 * x).exp();
+    let one_over_b_max = 1.0 / b_max;
+    ((if q < 0.0 { -1.0 } else { 1.0 }) * (b_max - one_over_b_max))
+        .max(0.0)
         .abs()
 }
 
diff --git a/src/lets_be_rational/mod.rs b/src/lets_be_rational/mod.rs
index eb07fb4..50a1ac0 100644
--- a/src/lets_be_rational/mod.rs
+++ b/src/lets_be_rational/mod.rs
@@ -1,5 +1,3 @@
-use num_traits::{AsPrimitive, Float, FloatConst, FromPrimitive};
-
 use crate::lets_be_rational::black::normalised_black;
 use crate::OptionType;
 
@@ -46,26 +44,23 @@ pub(crate) const ONE_OVER_SQRT_TWO_PI: f64 = 1.0 / SQRT_TWO_PI;
 /// The function uses the natural logarithm of the forward price over the strike price,
 /// multiplies it by the square root of time to maturity, and applies the option type
 /// to determine the final price. It's suitable for European options *only*.
-pub fn black<T>(
-    forward_price: T,
-    strike_price: T,
-    sigma: T,
-    time_to_maturity: T,
+pub fn black(
+    forward_price: f64,
+    strike_price: f64,
+    sigma: f64,
+    time_to_maturity: f64,
     option_type: OptionType,
-) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    let q: T = T::from::<f64>(option_type.into()).unwrap();
-    let intrinsic = (if q < T::zero() {
+) -> f64 {
+    let q: f64 = option_type.into();
+    let intrinsic = (if q < 0.0 {
         strike_price - forward_price
     } else {
         forward_price - strike_price
     })
-    .max(T::zero())
+    .max(0.0)
     .abs();
     // Map in-the-money to out-of-the-money
-    if q * (forward_price - strike_price) > T::zero() {
+    if q * (forward_price - strike_price) > 0.0 {
         return intrinsic
             + black(
                 forward_price,
@@ -85,16 +80,13 @@ where
     )
 }
 
-pub fn implied_volatility_from_a_transformed_rational_guess<T>(
-    market_price: T,
-    forward_price: T,
-    strike_price: T,
-    time_to_maturity: T,
+pub fn implied_volatility_from_a_transformed_rational_guess(
+    market_price: f64,
+    forward_price: f64,
+    strike_price: f64,
+    time_to_maturity: f64,
     option_type: OptionType,
-) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
+) -> f64 {
     so_rational::implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(
         market_price,
         forward_price,
diff --git a/src/lets_be_rational/normal_distribution.rs b/src/lets_be_rational/normal_distribution.rs
index be481fe..a8d6959 100644
--- a/src/lets_be_rational/normal_distribution.rs
+++ b/src/lets_be_rational/normal_distribution.rs
@@ -1,26 +1,16 @@
-use num_traits::{AsPrimitive, Float, FromPrimitive};
 use once_cell::sync::Lazy;
 use statrs::distribution::{ContinuousCDF, Normal};
 
 static STANDARD_NORMAL: Lazy<Normal> = Lazy::new(|| Normal::new(0.0, 1.0).unwrap());
 
-pub fn standard_normal_cdf<T>(x: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    T::from(STANDARD_NORMAL.cdf(x.as_())).unwrap()
+pub fn standard_normal_cdf(x: f64) -> f64 {
+    STANDARD_NORMAL.cdf(x)
 }
 
-pub fn inverse_normal_cdf<T>(p: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    T::from(STANDARD_NORMAL.inverse_cdf(p.as_())).unwrap()
+pub fn inverse_normal_cdf(p: f64) -> f64 {
+    STANDARD_NORMAL.inverse_cdf(p)
 }
 
-pub fn inverse_f_upper_map<T>(f: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    T::from(-2.0 * inverse_normal_cdf(f.as_())).unwrap()
+pub fn inverse_f_upper_map(f: f64) -> f64 {
+    -2.0 * inverse_normal_cdf(f)
 }
diff --git a/src/lets_be_rational/rational_cubic.rs b/src/lets_be_rational/rational_cubic.rs
index f5619c6..0da161d 100644
--- a/src/lets_be_rational/rational_cubic.rs
+++ b/src/lets_be_rational/rational_cubic.rs
@@ -1,21 +1,18 @@
-use num_traits::{Float, FromPrimitive};
+use num_traits::Zero;
 
 const MINIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE: f64 = -(1.0 - 1.4901161193847656e-8); // -(1.0 - f64::EPSILON.sqrt());
 const MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE: f64 = 2.0 / (f64::EPSILON * f64::EPSILON);
 
-pub fn rational_cubic_interpolation<T>(
-    x: T,
-    x_l: T,
-    x_r: T,
-    y_l: T,
-    y_r: T,
-    d_l: T,
-    d_r: T,
-    r: T,
-) -> Result<T, String>
-where
-    T: Float + FromPrimitive,
-{
+pub fn rational_cubic_interpolation(
+    x: f64,
+    x_l: f64,
+    x_r: f64,
+    y_l: f64,
+    y_r: f64,
+    d_l: f64,
+    d_r: f64,
+    r: f64,
+) -> Result<f64, String> {
     // TODO: verify is it necessary to check for infinite values
     // or ensure that they are not present in the input
     if x.is_infinite() || x_l.is_infinite() || x_r.is_infinite() {
@@ -25,16 +22,16 @@ where
     let h = x_r - x_l;
     // NOTE: This is an optimization as reuse calculated value of `h`.
     // Based on `additional_benches_to_verify::abs_vs_equality_benchmarks` as an evidence
-    if h.abs() <= T::epsilon() {
-        return Ok(T::from(0.5).unwrap() * (y_l + y_r));
+    if h.abs() <= f64::EPSILON {
+        return Ok(0.5 * (y_l + y_r));
     }
 
     let t = (x - x_l) / h;
-    let omt = T::one() - t;
+    let omt = 1.0 - t;
 
     // r should be greater than -1.
     // We do not use assert(r > -1) here in order to allow values such as NaN to be propagated as they should.
-    if r < T::from(MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE).unwrap() {
+    if r < MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE {
         let t_power = t * t;
         let omt2 = omt * omt;
 
@@ -43,7 +40,7 @@ where
             + (r * y_l + h * d_l) * t * omt2
             + y_l * omt2 * omt;
 
-        let denominator = T::one() + (r - T::from(3.0).unwrap()) * t * omt;
+        let denominator = 1.0 + (r - 3.0) * t * omt;
 
         // Formula (2.4) divided by formula (2.5)
         Ok(numerator / denominator)
@@ -54,20 +51,17 @@ where
     }
 }
 
-fn minimum_rational_cubic_control_parameter<T>(
-    d_l: T,
-    d_r: T,
-    s: T,
+fn minimum_rational_cubic_control_parameter(
+    d_l: f64,
+    d_r: f64,
+    s: f64,
     prefer_shape_preservation_over_smoothness: bool,
-) -> T
-where
-    T: Float + FromPrimitive,
-{
-    let monotonic = d_l * s >= T::zero() && d_r * s >= T::zero();
+) -> f64 {
+    let monotonic = d_l * s >= 0.0 && d_r * s >= 0.0;
     let convex = d_l <= s && s <= d_r;
     let concave = d_l >= s && s >= d_r;
     if !monotonic && !convex && !concave {
-        return T::from(MINIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE).unwrap();
+        return MINIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE;
     }
     let d_r_m_d_l = d_r - d_l;
     let d_r_m_s = d_r - s;
@@ -78,9 +72,9 @@ where
     let r1 = match (monotonic, s.is_zero()) {
         (true, false) => (d_r + d_l) / s,
         (true, true) if prefer_shape_preservation_over_smoothness => {
-            T::from(MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE).unwrap()
+            MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE
         }
-        _ => T::min_value(),
+        _ => f64::MIN,
     };
 
     let r2 = match (
@@ -89,15 +83,15 @@ where
         prefer_shape_preservation_over_smoothness,
     ) {
         (true, _, _) if !s_m_d_l.is_zero() && !d_r_m_s.is_zero() => {
-            T::max((d_r_m_d_l / d_r_m_s).abs(), (d_r_m_d_l / s_m_d_l).abs())
+            f64::max((d_r_m_d_l / d_r_m_s).abs(), (d_r_m_d_l / s_m_d_l).abs())
         }
-        (_, true, true) => T::from(MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE).unwrap(),
-        _ => T::min_value(),
+        (_, true, true) => MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE,
+        _ => f64::MIN,
     };
 
-    T::max(
-        T::from(MINIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE).unwrap(),
-        T::max(r1, r2),
+    f64::max(
+        MINIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE,
+        f64::max(r1, r2),
     )
 }
 
@@ -106,23 +100,20 @@ pub enum Side {
     Right,
 }
 
-fn rational_cubic_control_parameter<T>(
-    x_l: T,
-    x_r: T,
-    y_l: T,
-    y_r: T,
-    d_l: T,
-    d_r: T,
-    second_derivative: T,
+fn rational_cubic_control_parameter(
+    x_l: f64,
+    x_r: f64,
+    y_l: f64,
+    y_r: f64,
+    d_l: f64,
+    d_r: f64,
+    second_derivative: f64,
     side: Side,
-) -> T
-where
-    T: Float + FromPrimitive,
-{
+) -> f64 {
     let h = x_r - x_l;
-    let numerator = T::from(0.5).unwrap() * second_derivative.mul_add(h, d_r - d_l);
+    let numerator = 0.5 * second_derivative.mul_add(h, d_r - d_l);
     if numerator.is_zero() {
-        return T::zero();
+        return 0.0;
     }
 
     let denominator = match side {
@@ -132,28 +123,25 @@ where
 
     match (
         denominator.is_zero() || denominator.is_infinite(),
-        numerator > T::zero(),
+        numerator > 0.0,
     ) {
-        (true, true) => T::from(MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE).unwrap(),
-        (true, false) => T::from(MINIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE).unwrap(),
+        (true, true) => MAXIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE,
+        (true, false) => MINIMUM_RATIONAL_CUBIC_CONTROL_PARAMETER_VALUE,
         _ => numerator / denominator,
     }
 }
 
-pub fn convex_rational_cubic_control_parameter<T>(
-    x_l: T,
-    x_r: T,
-    y_l: T,
-    y_r: T,
-    d_l: T,
-    d_r: T,
-    second_derivative: T,
+pub fn convex_rational_cubic_control_parameter(
+    x_l: f64,
+    x_r: f64,
+    y_l: f64,
+    y_r: f64,
+    d_l: f64,
+    d_r: f64,
+    second_derivative: f64,
     prefer_shape_preservation_over_smoothness: bool,
     side: Side,
-) -> T
-where
-    T: Float + FromPrimitive,
-{
+) -> f64 {
     let r = rational_cubic_control_parameter(x_l, x_r, y_l, y_r, d_l, d_r, second_derivative, side);
     let r_min = minimum_rational_cubic_control_parameter(
         d_l,
diff --git a/src/lets_be_rational/so_rational.rs b/src/lets_be_rational/so_rational.rs
index 82fa760..de77568 100644
--- a/src/lets_be_rational/so_rational.rs
+++ b/src/lets_be_rational/so_rational.rs
@@ -9,7 +9,9 @@ use crate::lets_be_rational::rational_cubic::{
 use crate::lets_be_rational::{DENORMALISATION_CUTOFF, ONE_OVER_SQRT_TWO_PI};
 use crate::OptionType;
 
-use num_traits::{AsPrimitive, Float, FloatConst, FromPrimitive};
+const VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_ABOVE_MAXIMUM: f64 = f64::MAX;
+
+const VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_BELOW_INTRINSIC: f64 = -f64::MAX;
 
 const SQRT_DBL_MIN: f64 = 1.4916681462400413e-154;
 const SQRT_DBL_MAX: f64 = 1.340_780_792_994_259_6e154;
@@ -18,148 +20,110 @@ const SQRT_THREE: f64 = 1.732_050_807_568_877_2; //3.0_f64.sqrt();
 
 const TWO_PI_OVER_SQRT_TWENTY_SEVEN: f64 = 1.209_199_576_156_145_2; // f64::PI() * 2.0 / sqrt(27.0);
 
-const SQRT_PI_OVER_TWO: f64 = statrs::consts::SQRT_2PI / 2.; //sqrt(f64::PI() / 2.0_f64);
+const SQRT_PI_OVER_TWO: f64 = 1.253_314_137_315_500_3; //sqrt(f64::PI() / 2.0_f64);
 
 const SQRT_ONE_OVER_THREE: f64 = 0.577_350_269_189_625_7; // sqrt(1.0 / 3.0_f64);
 
 const PI_OVER_SIX: f64 = std::f64::consts::PI / 6.0;
 
-fn is_below_horizon<T>(x: T) -> bool
-where
-    T: Float + FromPrimitive,
-{
-    x.abs() < T::from(DENORMALISATION_CUTOFF).unwrap()
+fn is_below_horizon(x: f64) -> bool {
+    x.abs() < DENORMALISATION_CUTOFF
 }
 
-pub(crate) fn normalised_vega<T>(x: T, s: T) -> T
-where
-    T: Float + FromPrimitive,
-{
+pub(crate) fn normalised_vega(x: f64, s: f64) -> f64 {
     let ax = x.abs();
-    if ax <= T::zero() {
-        T::from(ONE_OVER_SQRT_TWO_PI).unwrap() * (T::from(-0.125).unwrap() * s * s).exp()
-    } else if s <= T::zero() || s <= ax * T::from(SQRT_DBL_MIN).unwrap() {
-        T::zero()
+    if ax <= 0.0 {
+        ONE_OVER_SQRT_TWO_PI * (-0.125 * s * s).exp()
+    } else if s <= 0.0 || s <= ax * SQRT_DBL_MIN {
+        0.0
     } else {
-        T::from(ONE_OVER_SQRT_TWO_PI).unwrap()
-            * (T::from(-0.5).unwrap() * ((x / s).powi(2) + (T::from(0.5).unwrap() * s).powi(2)))
-                .exp()
+        ONE_OVER_SQRT_TWO_PI * (-0.5 * ((x / s).powi(2) + (0.5 * s).powi(2))).exp()
     }
 }
 
-fn householder_factor<T>(newton: T, halley: T, hh3: T) -> T
-where
-    T: Float + FromPrimitive,
-{
-    (T::one() + T::from(0.5).unwrap() * halley * newton)
-        / (T::one() + newton * (halley + hh3 * newton / T::from(6.0).unwrap()))
+fn householder_factor(newton: f64, halley: f64, hh3: f64) -> f64 {
+    (1.0 + 0.5 * halley * newton) / (1.0 + newton * (halley + hh3 * newton / 6.0))
 }
 
-fn inverse_f_lower_map<T>(x: T, f: T) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
+fn inverse_f_lower_map(x: f64, f: f64) -> f64 {
     if is_below_horizon(f) {
-        T::zero()
+        0.0
     } else {
-        (x / (T::from(SQRT_THREE).unwrap()
-            * T::from(inverse_normal_cdf(
-                (f.as_() / (TWO_PI_OVER_SQRT_TWENTY_SEVEN * x.as_().abs())).powf(1.0 / 3.0),
-            ))
-            .unwrap()))
+        (x / (SQRT_THREE
+            * inverse_normal_cdf((f / (TWO_PI_OVER_SQRT_TWENTY_SEVEN * x.abs())).powf(1.0 / 3.0))))
         .abs()
     }
 }
 
-fn compute_f_upper_map_and_first_two_derivatives<T>(x: T, s: T) -> (T, T, T)
-where
-    T: Float + FromPrimitive + AsPrimitive<f64>,
-{
-    let f = T::from(standard_normal_cdf(-0.5 * s.as_())).unwrap();
+fn compute_f_upper_map_and_first_two_derivatives(x: f64, s: f64) -> (f64, f64, f64) {
+    let f = standard_normal_cdf(-0.5 * s);
     let (fp, fpp);
 
     if is_below_horizon(x) {
-        fp = T::from(-0.5).unwrap();
-        fpp = T::zero();
+        fp = -0.5;
+        fpp = 0.0;
     } else {
         let w = (x / s).powi(2);
-        fp = T::from(-0.5).unwrap() * (T::from(0.5).unwrap() * w).exp();
-        fpp = T::from(SQRT_PI_OVER_TWO).unwrap() * (w + T::from(0.125).unwrap() * s * s).exp() * w
-            / s;
+        fp = -0.5 * (0.5 * w).exp();
+        fpp = SQRT_PI_OVER_TWO * (w + 0.125 * s * s).exp() * w / s;
     }
 
     (f, fp, fpp)
 }
 
-fn compute_f_lower_map_and_first_two_derivatives<T>(x: T, s: T) -> (T, T, T)
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
+fn compute_f_lower_map_and_first_two_derivatives(x: f64, s: f64) -> (f64, f64, f64) {
     let ax = x.abs();
-    let z = T::from(SQRT_ONE_OVER_THREE).unwrap() * ax / s;
+    let z = SQRT_ONE_OVER_THREE * ax / s;
     let y = z * z;
     let s2 = s * s;
-    let phi = T::from(standard_normal_cdf(z.as_())).unwrap();
-    let phi_ = T::from(standard_normal_cdf(-z.as_())).unwrap();
-    let fpp = T::from(PI_OVER_SIX).unwrap() * y / (s2 * s)
+    let phi = standard_normal_cdf(z);
+    let phi_ = standard_normal_cdf(-z);
+    let fpp = PI_OVER_SIX * y / (s2 * s)
         * phi_
-        * (T::from(8.0).unwrap() * T::from(SQRT_THREE).unwrap() * s * ax
-            + (T::from(3.0).unwrap() * s2 * (s2 - T::from(8.0).unwrap())
-                - T::from(8.0).unwrap() * x * x)
-                * phi_
-                / phi)
-        * (T::from(2.0).unwrap() * y + T::from(0.25).unwrap() * s2).exp();
+        * (8.0 * SQRT_THREE * s * ax + (3.0 * s2 * (s2 - 8.0) - 8.0 * x * x) * phi_ / phi)
+        * (2.0 * y + 0.25 * s2).exp();
 
     let (fp, f);
 
     if is_below_horizon(s) {
-        fp = T::one();
-        f = T::zero();
+        fp = 1.0;
+        f = 0.0;
     } else {
         let phi2 = phi_ * phi_;
-        fp = T::PI()
-            * T::from(2.0).unwrap()
-            * y
-            * phi2
-            * (y + T::from(0.125).unwrap() * s * s).exp();
+        fp = std::f64::consts::PI * 2.0 * y * phi2 * (y + 0.125 * s * s).exp();
         f = if is_below_horizon(x) {
-            T::zero()
+            0.0
         } else {
-            T::from(TWO_PI_OVER_SQRT_TWENTY_SEVEN).unwrap() * ax * (phi2 * phi_)
+            TWO_PI_OVER_SQRT_TWENTY_SEVEN * ax * (phi2 * phi_)
         };
     }
 
     (f, fp, fpp)
 }
 
-pub(crate) fn implied_volatility_from_a_transformed_rational_guess_with_limited_iterations<T>(
-    market_price: T,
-    forward_price: T,
-    strike_price: T,
-    time_to_maturity: T,
+pub(crate) fn implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(
+    market_price: f64,
+    forward_price: f64,
+    strike_price: f64,
+    time_to_maturity: f64,
     mut option_type: OptionType,
     max_iteration: i32,
-) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
-    let q = T::from(option_type as i32).unwrap();
+) -> f64 {
+    let q = option_type as i32 as f64;
     let mut price = market_price;
-    let intrinsic = (q * (forward_price - strike_price)).max(T::zero()).abs();
+    let intrinsic = (q as i32 as f64 * (forward_price - strike_price))
+        .max(0.0)
+        .abs();
     if price < intrinsic {
-        return T::min_value();
+        return VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_BELOW_INTRINSIC;
     }
-    let max_price = if q < T::zero() {
-        strike_price
-    } else {
-        forward_price
-    };
+    let max_price = if q < 0.0 { strike_price } else { forward_price };
     if price >= max_price {
-        return T::max_value();
+        return VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_ABOVE_MAXIMUM;
     }
     let x = (forward_price / strike_price).ln();
-    if q * x > T::zero() {
-        price = (price - intrinsic).max(T::zero()).abs();
+    if q as i32 as f64 * x > 0.0 {
+        price = (price - intrinsic).max(0.0).abs();
         option_type = -option_type;
     }
     unchecked_normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(
@@ -167,70 +131,59 @@ where
     ) / time_to_maturity.sqrt()
 }
 
-#[allow(dead_code)]
-fn normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations<T>(
-    beta: T,
-    x: T,
+fn normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(
+    beta: f64,
+    x: f64,
     option_type: OptionType,
     max_iteration: i32,
-) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
+) -> f64 {
     // Map in-the-money to out-of-the-money
     let mut beta = beta;
     let mut q = option_type;
-    if T::from(q as i32).unwrap() * x > T::zero() {
-        beta = beta - normalised_intrinsic(x, q);
+    if q as i32 as f64 * x > 0.0 {
+        beta -= normalised_intrinsic(x, q);
         q = -q;
     }
-    if beta < T::zero() {
-        return T::min_value();
+    if beta < 0.0 {
+        return VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_BELOW_INTRINSIC;
     }
 
     unchecked_normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(beta, x, q, max_iteration)
 }
 
-pub(crate) fn unchecked_normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations<
-    T,
->(
-    mut beta: T,
-    mut x: T,
+pub(crate) fn unchecked_normalised_implied_volatility_from_a_transformed_rational_guess_with_limited_iterations(
+    mut beta: f64,
+    mut x: f64,
     option_type: OptionType,
     n: i32,
-) -> T
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
-    if T::from(option_type as i32).unwrap() * x > T::zero() {
-        beta = (beta - normalised_intrinsic(x, option_type))
-            .abs()
-            .max(T::zero());
+) -> f64 {
+    if option_type as i32 as f64 * x > 0.0 {
+        beta = (beta - normalised_intrinsic(x, option_type)).abs().max(0.0);
     }
     if option_type == OptionType::Put {
         x = -x;
     }
-    if beta <= T::zero() {
-        return T::zero();
+    if beta <= 0.0 {
+        return 0.0;
     }
-    if beta < T::from(DENORMALISATION_CUTOFF).unwrap() {
-        return T::zero();
+    if beta < DENORMALISATION_CUTOFF {
+        return 0.0;
     }
-    let b_max = (T::from(0.5).unwrap() * x).exp();
+    let b_max = (0.5 * x).exp();
     if beta >= b_max {
-        return T::max_value();
+        return VOLATILITY_VALUE_TO_SIGNAL_PRICE_IS_ABOVE_MAXIMUM;
     }
 
     let iterations = 0;
     let mut direction_reversal_count = 0;
-    let mut f = -T::max_value();
-    let mut s = -T::max_value();
+    let mut f = -f64::MAX;
+    let mut s = -f64::MAX;
     let mut ds = s;
-    let mut ds_previous = T::zero();
-    let mut s_left = T::min_value();
-    let mut s_right = T::max_value();
+    let mut ds_previous = 0.0;
+    let mut s_left = f64::MIN;
+    let mut s_right = f64::MAX;
 
-    let s_c = (T::from(2.0).unwrap() * x.abs()).sqrt();
+    let s_c = (2.0 * x.abs()).sqrt();
     let b_c = normalised_black_call(x, s_c);
     let v_c = normalised_vega(x, s_c);
 
@@ -241,11 +194,11 @@ where
             let (f_lower_map_l, d_f_lower_map_l_d_beta, d2_f_lower_map_l_d_beta2) =
                 compute_f_lower_map_and_first_two_derivatives(x, s_l);
             let r_ll = convex_rational_cubic_control_parameter(
-                T::zero(),
+                0.0,
                 b_l,
-                T::zero(),
+                0.0,
                 f_lower_map_l,
-                T::one(),
+                1.0,
                 d_f_lower_map_l_d_beta,
                 d2_f_lower_map_l_d_beta2,
                 true,
@@ -254,37 +207,37 @@ where
             // TODO: Expect terrible approach, handle it properly
             f = rational_cubic_interpolation(
                 beta,
-                T::zero(),
+                0.0,
                 b_l,
-                T::zero(),
+                0.0,
                 f_lower_map_l,
-                T::one(),
+                1.0,
                 d_f_lower_map_l_d_beta,
                 r_ll,
             )
             .expect("We should expect correct parameters");
-            if f <= T::zero() {
+            if f <= 0.0 {
                 let t = beta / b_l;
-                f = (f_lower_map_l * t + b_l * (T::one() - t)) * t;
+                f = (f_lower_map_l * t + b_l * (1.0 - t)) * t;
             }
             s = inverse_f_lower_map(x, f);
             s_right = s_l;
 
             for _ in 0..n {
-                if ds.abs() <= T::epsilon() * s {
+                if ds.abs() <= f64::EPSILON * s {
                     break;
                 }
-                if ds * ds_previous < T::zero() {
+                if ds * ds_previous < 0.0 {
                     direction_reversal_count += 1;
                 }
                 if iterations > 0 && (direction_reversal_count == 3 || !(s > s_left && s < s_right))
                 {
-                    s = T::from(0.5).unwrap() * (s_left + s_right);
-                    if s_right - s_left <= T::epsilon() * s {
+                    s = 0.5 * (s_left + s_right);
+                    if s_right - s_left <= f64::EPSILON * s {
                         break;
                     }
                     direction_reversal_count = 0;
-                    ds = T::zero();
+                    ds = 0.0;
                 }
                 ds_previous = ds;
                 let b = normalised_black_call(x, s);
@@ -294,31 +247,22 @@ where
                 } else if b < beta && s > s_left {
                     s_left = s;
                 }
-                if b <= T::zero() || bp <= T::zero() {
-                    ds = T::from(0.5).unwrap() * (s_left + s_right) - s;
+                if b <= 0.0 || bp <= 0.0 {
+                    ds = 0.5 * (s_left + s_right) - s;
                 } else {
                     let ln_b = b.ln();
                     let ln_beta = beta.ln();
                     let bpob = bp / b;
                     let h = x / s;
-                    let b_halley = h * h / s - s / T::from(4.0).unwrap();
+                    let b_halley = h * h / s - s / 4.0;
                     let newton = (ln_beta - ln_b) * ln_b / ln_beta / bpob;
-                    let halley = b_halley - bpob * (T::one() + T::from(2.0).unwrap() / ln_b);
-                    let b_hh3 = b_halley * b_halley
-                        - T::from(3.0).unwrap() * (h / s).powi(2)
-                        - T::from(0.25).unwrap();
-                    let hh3 = b_hh3
-                        + T::from(2.0).unwrap()
-                            * bpob.powi(2)
-                            * (T::one()
-                                + T::from(3.0).unwrap() / ln_b * (T::one() + T::one() / ln_b))
-                        - T::from(3.0).unwrap()
-                            * b_halley
-                            * bpob
-                            * (T::one() + T::from(2.0).unwrap() / ln_b);
+                    let halley = b_halley - bpob * (1.0 + 2.0 / ln_b);
+                    let b_hh3 = b_halley * b_halley - 3.0 * (h / s).powi(2) - 0.25;
+                    let hh3 = b_hh3 + 2.0 * bpob.powi(2) * (1.0 + 3.0 / ln_b * (1.0 + 1.0 / ln_b))
+                        - 3.0 * b_halley * bpob * (1.0 + 2.0 / ln_b);
                     ds = newton * householder_factor(newton, halley, hh3);
                 }
-                s = s + ds.max(-T::from(0.5).unwrap() * s);
+                s += ds.max(-0.5 * s);
             }
             return s;
         } else {
@@ -328,29 +272,20 @@ where
                 b_c,
                 s_l,
                 s_c,
-                T::one() / v_l,
-                T::one() / v_c,
-                T::zero(),
+                1.0 / v_l,
+                1.0 / v_c,
+                0.0,
                 false,
                 Side::Right,
             );
             // TODO: Expect terrible approach, handle it properly
-            s = rational_cubic_interpolation(
-                beta,
-                b_l,
-                b_c,
-                s_l,
-                s_c,
-                T::one() / v_l,
-                T::one() / v_c,
-                r_lm,
-            )
-            .expect("We should expect correct parameters");
+            s = rational_cubic_interpolation(beta, b_l, b_c, s_l, s_c, 1.0 / v_l, 1.0 / v_c, r_lm)
+                .expect("We should expect correct parameters");
             s_left = s_l;
             s_right = s_c;
         }
     } else {
-        let s_h = if v_c > T::min_value() {
+        let s_h = if v_c > f64::MIN {
             s_c + (b_max - b_c) / v_c
         } else {
             s_c
@@ -363,39 +298,28 @@ where
                 b_h,
                 s_c,
                 s_h,
-                T::one() / v_c,
-                T::one() / v_h,
-                T::zero(),
+                1.0 / v_c,
+                1.0 / v_h,
+                0.0,
                 false,
                 Side::Left,
             );
             // TODO: Expect terrible approach, handle it properly
-            s = rational_cubic_interpolation(
-                beta,
-                b_c,
-                b_h,
-                s_c,
-                s_h,
-                T::one() / v_c,
-                T::one() / v_h,
-                r_hm,
-            )
-            .expect("We should expect correct parameters");
+            s = rational_cubic_interpolation(beta, b_c, b_h, s_c, s_h, 1.0 / v_c, 1.0 / v_h, r_hm)
+                .expect("We should expect correct parameters");
             s_left = s_c;
             s_right = s_h;
         } else {
             let (f_upper_map_h, d_f_upper_map_h_d_beta, d2_f_upper_map_h_d_beta2) =
                 compute_f_upper_map_and_first_two_derivatives(x, s_h);
-            if d2_f_upper_map_h_d_beta2 > T::from(-SQRT_DBL_MAX).unwrap()
-                && d2_f_upper_map_h_d_beta2 < T::from(SQRT_DBL_MAX).unwrap()
-            {
+            if d2_f_upper_map_h_d_beta2 > -SQRT_DBL_MAX && d2_f_upper_map_h_d_beta2 < SQRT_DBL_MAX {
                 let r_hh = convex_rational_cubic_control_parameter(
                     b_h,
                     b_max,
                     f_upper_map_h,
-                    T::zero(),
+                    0.0,
                     d_f_upper_map_h_d_beta,
-                    T::from(-0.5).unwrap(),
+                    -0.5,
                     d2_f_upper_map_h_d_beta2,
                     true,
                     Side::Left,
@@ -406,38 +330,37 @@ where
                     b_h,
                     b_max,
                     f_upper_map_h,
-                    T::zero(),
+                    0.0,
                     d_f_upper_map_h_d_beta,
-                    -T::from(0.5).unwrap(),
+                    -0.5,
                     r_hh,
                 )
                 .expect("We should expect correct parameters");
             }
-            if f <= T::zero() {
+            if f <= 0.0 {
                 let h = b_max - b_h;
                 let t = (beta - b_h) / h;
-                f = (f_upper_map_h * (T::one() - t) + T::from(0.5).unwrap() * h * t)
-                    * (T::one() - t);
+                f = (f_upper_map_h * (1.0 - t) + 0.5 * h * t) * (1.0 - t);
             }
             s = inverse_f_upper_map(f);
             s_left = s_h;
-            if beta > T::from(0.5).unwrap() * b_max {
+            if beta > 0.5 * b_max {
                 for _ in 0..n {
-                    if ds.abs() <= T::epsilon() * s {
+                    if ds.abs() <= f64::EPSILON * s {
                         break;
                     }
-                    if ds * ds_previous < T::zero() {
+                    if ds * ds_previous < 0.0 {
                         direction_reversal_count += 1;
                     }
                     if iterations > 0
                         && (direction_reversal_count == 3 || !(s > s_left && s < s_right))
                     {
-                        s = T::from(0.5).unwrap() * (s_left + s_right);
-                        if s_right - s_left <= T::epsilon() * s {
+                        s = 0.5 * (s_left + s_right);
+                        if s_right - s_left <= f64::EPSILON * s {
                             break;
                         }
                         direction_reversal_count = 0;
-                        ds = T::zero();
+                        ds = 0.0;
                     }
                     ds_previous = ds;
                     let b = normalised_black_call(x, s);
@@ -447,23 +370,20 @@ where
                     } else if b < beta && s > s_left {
                         s_left = s;
                     }
-                    if b >= b_max || bp <= T::min_value() {
-                        ds = T::from(0.5).unwrap() * (s_left + s_right) - s;
+                    if b >= b_max || bp <= f64::MIN {
+                        ds = 0.5 * (s_left + s_right) - s;
                     } else {
                         let b_max_minus_b = b_max - b;
                         let g = ((b_max - beta) / b_max_minus_b).ln();
                         let gp = bp / b_max_minus_b;
-                        let b_halley = (x / s).powi(2) / s - s / T::from(4.0).unwrap();
-                        let b_hh3 = b_halley * b_halley
-                            - T::from(3.0).unwrap() * (x / (s * s)).powi(2)
-                            - T::from(0.25).unwrap();
+                        let b_halley = (x / s).powi(2) / s - s / 4.0;
+                        let b_hh3 = b_halley * b_halley - 3.0 * (x / (s * s)).powi(2) - 0.25;
                         let newton = -g / gp;
                         let halley = b_halley + gp;
-                        let hh3 = b_hh3
-                            + gp * (T::from(2.0).unwrap() * gp + T::from(3.0).unwrap() * b_halley);
+                        let hh3 = b_hh3 + gp * (2.0 * gp + 3.0 * b_halley);
                         ds = newton * householder_factor(newton, halley, hh3);
                     }
-                    s = s + ds.max(-T::from(0.5).unwrap() * s);
+                    s += ds.max(-0.5 * s);
                 }
                 return s;
             }
@@ -471,19 +391,19 @@ where
     }
 
     for _ in 0..n {
-        if ds.abs() <= T::epsilon() * s {
+        if ds.abs() <= f64::EPSILON * s {
             break;
         }
-        if ds * ds_previous < T::zero() {
+        if ds * ds_previous < 0.0 {
             direction_reversal_count += 1;
         }
         if iterations > 0 && (direction_reversal_count == 3 || !(s > s_left && s < s_right)) {
-            s = T::from(0.5).unwrap() * (s_left + s_right);
-            if s_right - s_left <= T::epsilon() * s {
+            s = 0.5 * (s_left + s_right);
+            if s_right - s_left <= f64::EPSILON * s {
                 break;
             }
             direction_reversal_count = 0;
-            ds = T::zero();
+            ds = 0.0;
         }
         ds_previous = ds;
         let b = normalised_black_call(x, s);
@@ -494,12 +414,10 @@ where
             s_left = s;
         }
         let newton = (beta - b) / bp;
-        let halley = (x / s).powi(2) / s - s / T::from(4.0).unwrap();
-        let hh3 = halley * halley
-            - T::from(3.0).unwrap() * (x / (s * s)).powi(2)
-            - T::from(0.25).unwrap();
-        ds = newton * householder_factor(newton, halley, hh3).max(-T::from(0.5).unwrap() * s);
-        s = s + ds;
+        let halley = (x / s).powi(2) / s - s / 4.0;
+        let hh3 = halley * halley - 3.0 * (x / (s * s)).powi(2) - 0.25;
+        ds = newton * householder_factor(newton, halley, hh3).max(-0.5 * s);
+        s += ds;
     }
 
     s
diff --git a/src/lib.rs b/src/lib.rs
index 6c981ef..de80c70 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,7 +16,9 @@
 //!
 //! See the [Github Repo](https://github.com/hayden4r4/blackscholes-rust/tree/master) for full source code.  Other implementations such as a [npm WASM package](https://www.npmjs.com/package/@haydenr4/blackscholes_wasm) and a [python module](https://pypi.org/project/blackscholes/) are also available.
 
-use num_traits::{Float, FromPrimitive, NumCast};
+pub use std::f32::consts::{E, PI};
+
+use num_traits::NumCast;
 use statrs::distribution::{ContinuousCDF, Normal};
 
 pub use greeks::Greeks;
@@ -30,30 +32,28 @@ mod inputs;
 pub mod lets_be_rational;
 mod pricing;
 
-pub(crate) const N_MEAN: f64 = 0.0;
-pub(crate) const N_STD_DEV: f64 = 1.0;
-pub(crate) const SQRT_2PI: f64 = statrs::consts::SQRT_2PI;
-pub(crate) const DAYS_PER_YEAR: f64 = 365.25;
+pub(crate) const N_MEAN: f32 = 0.0;
+pub(crate) const N_STD_DEV: f32 = 1.0;
+pub(crate) const SQRT_2PI: f32 = 2.5066282;
+pub(crate) const HALF: f32 = 0.5;
+pub(crate) const DAYS_PER_YEAR: f32 = 365.25;
 
-pub(crate) const A: f64 = 4.626_275_3e-1;
-pub(crate) const B: f64 = -1.168_519_2e-2;
-pub(crate) const C: f64 = 9.635_418_5e-4;
-pub(crate) const D: f64 = 7.535_022_5e-5;
-pub(crate) const _E: f64 = 1.424_516_45e-5;
-pub(crate) const F: f64 = -2.102_376_9e-5;
+pub(crate) const A: f32 = 4.626_275_3e-1;
+pub(crate) const B: f32 = -1.168_519_2e-2;
+pub(crate) const C: f32 = 9.635_418_5e-4;
+pub(crate) const D: f32 = 7.535_022_5e-5;
+pub(crate) const _E: f32 = 1.424_516_45e-5;
+pub(crate) const F: f32 = -2.102_376_9e-5;
 
 /// Calculates the d1 and d2 values for the option.
 /// # Requires
 /// s, k, r, q, t, sigma.
 /// # Returns
 /// Tuple (f32, f32) of (d1, d2)
-pub(crate) fn calc_d1d2<T>(inputs: &Inputs<T>) -> Result<(T, T), String>
-where
-    T: Float + FromPrimitive,
-{
+pub(crate) fn calc_d1d2(inputs: &Inputs) -> Result<(f32, f32), String> {
     let sigma = inputs
         .sigma
-        .ok_or("Expected Some(float) for self.sigma, received None")?;
+        .ok_or("Expected Some(f32) for self.sigma, received None")?;
     // Calculating numerator of d1
     let part1 = (inputs.s / inputs.k).ln();
 
@@ -61,11 +61,11 @@ where
         return Err("Log from s/k is infinity".to_string());
     }
 
-    let part2 = (inputs.r - inputs.q + (sigma.powi(2)) * T::from(0.5).unwrap()) * inputs.t;
+    let part2 = (inputs.r - inputs.q + (sigma.powi(2)) / 2.0) * inputs.t;
     let numd1 = part1 + part2;
 
     // Calculating denominator of d1 and d2
-    if inputs.t == T::zero() {
+    if inputs.t == 0.0 {
         return Err("Time to maturity is 0".to_string());
     }
 
@@ -82,14 +82,11 @@ where
 /// s, k, r, q, t, sigma
 /// # Returns
 /// Tuple (f32, f32) of (nd1, nd2)
-pub(crate) fn calc_nd1nd2<T>(inputs: &Inputs<T>) -> Result<(T, T), String>
-where
-    T: Float + FromPrimitive,
-{
+pub(crate) fn calc_nd1nd2(inputs: &Inputs) -> Result<(f32, f32), String> {
     let nd1nd2 = {
         let d1d2 = calc_d1d2(inputs)?;
 
-        let n: Normal = Normal::new(N_MEAN, N_STD_DEV).unwrap();
+        let n: Normal = Normal::new(N_MEAN as f64, N_STD_DEV as f64).unwrap();
 
         let num_cast_err: String = "Failed to cast f64 to f32".into();
         // Calculates the nd1 and nd2 values
@@ -115,23 +112,14 @@ where
 /// Calculates the n probability density function (PDF) for the given input.
 /// # Returns
 /// f32 of the value of the n probability density function.
-pub(crate) fn calc_npdf<T>(x: T) -> T
-where
-    T: Float + FromPrimitive,
-{
-    let n_mean = T::from(N_MEAN).unwrap();
-    let n_std_dev = T::from(N_STD_DEV).unwrap();
-    let sqrt_2pi = T::from(SQRT_2PI).unwrap();
-    let d: T = (x - n_mean) / n_std_dev;
-    (-T::from(0.5).unwrap() * d * d).exp() / (sqrt_2pi * n_std_dev)
+pub(crate) fn calc_npdf(x: f32) -> f32 {
+    let d: f32 = (x - N_MEAN) / N_STD_DEV;
+    (-HALF * d * d).exp() / (SQRT_2PI * N_STD_DEV)
 }
 
 /// # Returns
 /// f32 of the derivative of the nd1.
-pub fn calc_nprimed1<T>(inputs: &Inputs<T>) -> Result<T, String>
-where
-    T: Float + FromPrimitive,
-{
+pub fn calc_nprimed1(inputs: &Inputs) -> Result<f32, String> {
     let (d1, _) = calc_d1d2(inputs)?;
 
     // Get the standard n probability density function value of d1
@@ -141,10 +129,7 @@ where
 
 /// # Returns
 /// f32 of the derivative of the nd2.
-pub(crate) fn calc_nprimed2<T>(inputs: &Inputs<T>) -> Result<T, String>
-where
-    T: Float + FromPrimitive,
-{
+pub(crate) fn calc_nprimed2(inputs: &Inputs) -> Result<f32, String> {
     let (_, d2) = calc_d1d2(inputs)?;
 
     // Get the standard n probability density function value of d1
diff --git a/src/pricing.rs b/src/pricing.rs
index 3eb52e9..a4707df 100644
--- a/src/pricing.rs
+++ b/src/pricing.rs
@@ -1,4 +1,6 @@
-use num_traits::{float::FloatConst, AsPrimitive, Float};
+use std::f32::consts::E;
+
+use num_traits::Float;
 
 use crate::{lets_be_rational, Inputs, OptionType, *};
 
@@ -10,34 +12,29 @@ where
     fn calc_rational_price(&self) -> Result<f64, String>;
 }
 
-impl<T> Pricing<T> for Inputs<T>
-where
-    T: Float + FromPrimitive + AsPrimitive<f64> + FloatConst,
-{
+impl Pricing<f32> for Inputs {
     /// Calculates the price of the option.
     /// # Requires
     /// s, k, r, q, t, sigma.
     /// # Returns
-    /// T of the price of the option.
+    /// f32 of the price of the option.
     /// # Example
     /// ```
     /// use blackscholes::{Inputs, OptionType, Pricing};
     /// let inputs = Inputs::new(OptionType::Call, 100.0, 100.0, None, 0.05, 0.2, 20.0/365.25, Some(0.2));
     /// let price = inputs.calc_price().unwrap();
     /// ```
-    fn calc_price(&self) -> Result<T, String> {
+    fn calc_price(&self) -> Result<f32, String> {
         // Calculates the price of the option
-        let (nd1, nd2): (T, T) = calc_nd1nd2(self)?;
-        let price: T = match self.option_type {
-            OptionType::Call => T::max(
-                T::zero(),
-                nd1 * self.s * T::E().powf(-self.q * self.t)
-                    - nd2 * self.k * T::E().powf(-self.r * self.t),
+        let (nd1, nd2): (f32, f32) = calc_nd1nd2(self)?;
+        let price: f32 = match self.option_type {
+            OptionType::Call => f32::max(
+                0.0,
+                nd1 * self.s * E.powf(-self.q * self.t) - nd2 * self.k * E.powf(-self.r * self.t),
             ),
-            OptionType::Put => T::max(
-                T::zero(),
-                nd2 * self.k * T::E().powf(-self.r * self.t)
-                    - nd1 * self.s * T::E().powf(-self.q * self.t),
+            OptionType::Put => f32::max(
+                0.0,
+                nd2 * self.k * E.powf(-self.r * self.t) - nd1 * self.s * E.powf(-self.q * self.t),
             ),
         };
         Ok(price)
@@ -57,22 +54,22 @@ where
     fn calc_rational_price(&self) -> Result<f64, String> {
         let sigma = self
             .sigma
-            .ok_or("Expected Some(T) for self.sigma, received None")?;
+            .ok_or("Expected Some(f32) for self.sigma, received None")?;
 
         // let's be rational wants the forward price, not the spot price.
         let forward = self.s * ((self.r - self.q) * self.t).exp();
 
         // price using `black`
         let undiscounted_price = lets_be_rational::black(
-            forward.as_(),
-            self.k.as_(),
-            sigma.as_(),
-            self.t.as_(),
+            forward as f64,
+            self.k as f64,
+            sigma as f64,
+            self.t as f64,
             self.option_type,
         );
 
         // discount the price
-        let price = undiscounted_price * (-self.r.as_() * self.t.as_()).exp();
+        let price = undiscounted_price * (-self.r as f64 * self.t as f64).exp();
         Ok(price)
     }
 }


Test patch:
diff --git a/tests/test_implied_volatility.rs b/tests/test_implied_volatility.rs
index a4567a7..fb7383d 100644
--- a/tests/test_implied_volatility.rs
+++ b/tests/test_implied_volatility.rs
@@ -2,14 +2,13 @@ mod tests {
     use blackscholes::{ImpliedVolatility, Inputs, OptionType, Pricing};
 
     // Tolerance is a bit higher due to IV being an approximation
-    const TOLERANCE_F64: f64 = 1e-10;
-    const TOLERANCE_F32: f32 = 1e-5;
+    const TOLERANCE: f64 = 0.0001;
 
     #[test]
-    fn test_put_otm_rational_iv_f64() {
+    fn test_put_otm_rational_iv() {
         // arrange
-        let sigma: f64 = 0.25;
-        let mut inputs_put_otm: Inputs<f64> = Inputs {
+        let sigma: Option<f32> = Some(0.25);
+        let mut inputs_put_otm: Inputs = Inputs {
             option_type: OptionType::Put,
             s: 90.0,
             k: 100.0,
@@ -17,7 +16,7 @@ mod tests {
             r: 0.03,
             q: 0.02,
             t: 45.0 / 365.25,
-            sigma: Some(sigma),
+            sigma,
         };
 
         let price = inputs_put_otm.calc_price().unwrap();
@@ -30,70 +29,14 @@ mod tests {
 
         // assert
         println!("Put OTM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F64);
+        assert!((iv - sigma.unwrap() as f64).abs() < TOLERANCE);
     }
 
     #[test]
-    fn test_put_otm_rational_iv_f32() {
+    fn test_call_itm_rational_iv() {
         // arrange
-        let sigma: f32 = 0.25;
-        let mut inputs_put_otm: Inputs<f32> = Inputs {
-            option_type: OptionType::Put,
-            s: 90.0,
-            k: 100.0,
-            p: None,
-            r: 0.03,
-            q: 0.02,
-            t: 45.0 / 365.25,
-            sigma: Some(sigma),
-        };
-
-        let price = inputs_put_otm.calc_price().unwrap();
-
-        inputs_put_otm.p = Some(price);
-        inputs_put_otm.sigma = None;
-
-        // act
-        let iv = inputs_put_otm.calc_rational_iv().unwrap();
-
-        // assert
-        println!("Put OTM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F32);
-    }
-
-    #[test]
-    fn test_call_itm_rational_iv_f64() {
-        // arrange
-        let sigma: f64 = 0.15;
-        let mut inputs_call_itm: Inputs<f64> = Inputs {
-            option_type: OptionType::Call,
-            s: 120.0,
-            k: 100.0,
-            p: None,
-            r: 0.01,
-            q: 0.0,
-            t: 60.0 / 365.25,
-            sigma: Some(sigma),
-        };
-
-        let price = inputs_call_itm.calc_price().unwrap();
-
-        inputs_call_itm.p = Some(price);
-        inputs_call_itm.sigma = None;
-
-        // act
-        let iv = inputs_call_itm.calc_rational_iv().unwrap();
-
-        // assert
-        println!("Call ITM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F64);
-    }
-
-    #[test]
-    fn test_call_itm_rational_iv_f32() {
-        // arrange
-        let sigma: f32 = 0.15;
-        let mut inputs_call_itm: Inputs<f32> = Inputs {
+        let sigma: Option<f32> = Some(0.15);
+        let mut inputs_call_itm: Inputs = Inputs {
             option_type: OptionType::Call,
             s: 120.0,
             k: 100.0,
@@ -101,7 +44,7 @@ mod tests {
             r: 0.01,
             q: 0.0,
             t: 60.0 / 365.25,
-            sigma: Some(sigma),
+            sigma,
         };
 
         let price = inputs_call_itm.calc_price().unwrap();
@@ -114,14 +57,14 @@ mod tests {
 
         // assert
         println!("Call ITM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F32);
+        assert!((iv - sigma.unwrap() as f64).abs() < TOLERANCE);
     }
 
     #[test]
-    fn test_put_itm_rational_iv_f64() {
+    fn test_put_itm_rational_iv() {
         // arrange
-        let sigma: f64 = 0.18;
-        let mut inputs_put_itm: Inputs<f64> = Inputs {
+        let sigma: Option<f32> = Some(0.18);
+        let mut inputs_put_itm: Inputs = Inputs {
             option_type: OptionType::Put,
             s: 80.0,
             k: 100.0,
@@ -129,7 +72,7 @@ mod tests {
             r: 0.04,
             q: 0.03,
             t: 60.0 / 365.25,
-            sigma: Some(sigma),
+            sigma,
         };
 
         let price = inputs_put_itm.calc_price().unwrap();
@@ -142,42 +85,14 @@ mod tests {
 
         // assert
         println!("Put ITM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F64);
+        assert!((iv - sigma.unwrap() as f64).abs() < TOLERANCE);
     }
 
     #[test]
-    fn test_put_itm_rational_iv_f32() {
+    fn test_call_atm_rational_iv() {
         // arrange
-        let sigma: f32 = 0.18;
-        let mut inputs_put_itm: Inputs<f32> = Inputs {
-            option_type: OptionType::Put,
-            s: 80.0,
-            k: 100.0,
-            p: None,
-            r: 0.04,
-            q: 0.03,
-            t: 60.0 / 365.25,
-            sigma: Some(sigma),
-        };
-
-        let price = inputs_put_itm.calc_price().unwrap();
-
-        inputs_put_itm.p = Some(price);
-        inputs_put_itm.sigma = None;
-
-        // act
-        let iv = inputs_put_itm.calc_rational_iv().unwrap();
-
-        // assert
-        println!("Put ITM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F32);
-    }
-
-    #[test]
-    fn test_call_atm_rational_iv_f64() {
-        // arrange
-        let sigma: f64 = 0.2;
-        let mut inputs_call_atm: Inputs<f64> = Inputs {
+        let sigma: Option<f32> = Some(0.2);
+        let mut inputs_call_atm: Inputs = Inputs {
             option_type: OptionType::Call,
             s: 100.0,
             k: 100.0,
@@ -185,7 +100,7 @@ mod tests {
             r: 0.05,
             q: 0.04,
             t: 90.0 / 365.25,
-            sigma: Some(sigma),
+            sigma,
         };
 
         let price = inputs_call_atm.calc_price().unwrap();
@@ -198,70 +113,14 @@ mod tests {
 
         // assert
         println!("Call ATM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F64);
-    }
-
-    #[test]
-    fn test_call_atm_rational_iv_f32() {
-        // arrange
-        let sigma: f32 = 0.2;
-        let mut inputs_call_atm: Inputs<f32> = Inputs {
-            option_type: OptionType::Call,
-            s: 100.0,
-            k: 100.0,
-            p: None,
-            r: 0.05,
-            q: 0.04,
-            t: 90.0 / 365.25,
-            sigma: Some(sigma),
-        };
-
-        let price = inputs_call_atm.calc_price().unwrap();
-
-        inputs_call_atm.p = Some(price);
-        inputs_call_atm.sigma = None;
-
-        // act
-        let iv = inputs_call_atm.calc_rational_iv().unwrap();
-
-        // assert
-        println!("Call ATM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F32);
-    }
-
-    #[test]
-    fn test_put_atm_rational_iv_f64() {
-        // arrange
-        let sigma: f64 = 0.22;
-        let mut inputs_put_atm: Inputs<f64> = Inputs {
-            option_type: OptionType::Put,
-            s: 100.0,
-            k: 100.0,
-            p: None,
-            r: 0.06,
-            q: 0.01,
-            t: 120.0 / 365.25,
-            sigma: Some(sigma),
-        };
-
-        let price = inputs_put_atm.calc_price().unwrap();
-
-        inputs_put_atm.p = Some(price);
-        inputs_put_atm.sigma = None;
-
-        // act
-        let iv = inputs_put_atm.calc_rational_iv().unwrap();
-
-        // assert
-        println!("Put ATM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F64);
+        assert!((iv - sigma.unwrap() as f64).abs() < TOLERANCE);
     }
 
     #[test]
-    fn test_put_atm_rational_iv_f32() {
+    fn test_put_atm_rational_iv() {
         // arrange
-        let sigma: f32 = 0.22;
-        let mut inputs_put_atm: Inputs<f32> = Inputs {
+        let sigma: Option<f32> = Some(0.22);
+        let mut inputs_put_atm: Inputs = Inputs {
             option_type: OptionType::Put,
             s: 100.0,
             k: 100.0,
@@ -269,7 +128,7 @@ mod tests {
             r: 0.06,
             q: 0.01,
             t: 120.0 / 365.25,
-            sigma: Some(sigma),
+            sigma,
         };
 
         let price = inputs_put_atm.calc_price().unwrap();
@@ -282,6 +141,6 @@ mod tests {
 
         // assert
         println!("Put ATM: {}", iv);
-        assert!((iv - sigma).abs() < TOLERANCE_F32);
+        assert!((iv - sigma.unwrap() as f64).abs() < TOLERANCE);
     }
 }
diff --git a/tests/test_pricing.rs b/tests/test_pricing.rs
index daf57c8..4222fdf 100644
--- a/tests/test_pricing.rs
+++ b/tests/test_pricing.rs
@@ -1,6 +1,6 @@
 use blackscholes::{Inputs, OptionType, Pricing};
 
-const INPUTS_CALL_OTM: Inputs<f64> = Inputs {
+const INPUTS_CALL_OTM: Inputs = Inputs {
     option_type: OptionType::Call,
     s: 100.0,
     k: 110.0,
@@ -10,7 +10,7 @@ const INPUTS_CALL_OTM: Inputs<f64> = Inputs {
     t: 20.0 / 365.25,
     sigma: Some(0.2),
 };
-const INPUTS_CALL_ITM: Inputs<f64> = Inputs {
+const INPUTS_CALL_ITM: Inputs = Inputs {
     option_type: OptionType::Call,
     s: 100.0,
     k: 90.0,
@@ -20,7 +20,7 @@ const INPUTS_CALL_ITM: Inputs<f64> = Inputs {
     t: 20.0 / 365.25,
     sigma: Some(0.2),
 };
-const INPUTS_PUT_OTM: Inputs<f64> = Inputs {
+const INPUTS_PUT_OTM: Inputs = Inputs {
     option_type: OptionType::Put,
     s: 100.0,
     k: 90.0,
@@ -30,7 +30,7 @@ const INPUTS_PUT_OTM: Inputs<f64> = Inputs {
     t: 20.0 / 365.25,
     sigma: Some(0.2),
 };
-const INPUTS_PUT_ITM: Inputs<f64> = Inputs {
+const INPUTS_PUT_ITM: Inputs = Inputs {
     option_type: OptionType::Put,
     s: 100.0,
     k: 110.0,
@@ -62,22 +62,26 @@ fn price_put_itm() {
 fn price_using_lets_be_rational() {
     // compare the results from calc_price() and calc_rational_price() for the options above
     assert!(
-        (INPUTS_CALL_OTM.calc_price().unwrap() - INPUTS_CALL_OTM.calc_rational_price().unwrap())
+        (INPUTS_CALL_OTM.calc_price().unwrap()
+            - INPUTS_CALL_OTM.calc_rational_price().unwrap() as f32)
             .abs()
             < 0.001
     );
     assert!(
-        (INPUTS_CALL_ITM.calc_price().unwrap() - INPUTS_CALL_ITM.calc_rational_price().unwrap())
+        (INPUTS_CALL_ITM.calc_price().unwrap()
+            - INPUTS_CALL_ITM.calc_rational_price().unwrap() as f32)
             .abs()
             < 0.001
     );
     assert!(
-        (INPUTS_PUT_OTM.calc_price().unwrap() - INPUTS_PUT_OTM.calc_rational_price().unwrap())
+        (INPUTS_PUT_OTM.calc_price().unwrap()
+            - INPUTS_PUT_OTM.calc_rational_price().unwrap() as f32)
             .abs()
             < 0.001
     );
     assert!(
-        (INPUTS_PUT_ITM.calc_price().unwrap() - INPUTS_PUT_ITM.calc_rational_price().unwrap())
+        (INPUTS_PUT_ITM.calc_price().unwrap()
+            - INPUTS_PUT_ITM.calc_rational_price().unwrap() as f32)
             .abs()
             < 0.001
     );