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

Issue:
Increase test coverage
We have pretty limited test coverage at the moment, only covering the pricing api.  

Would love to expand test coverage to the IV calcs and greeks.  Some of this is difficult as we have some little known greeks included, that could make it difficult to find a reliable library to test against.  

Would appreciate any help in either recommending a trusted library to test against, or in actually expanding out the test coverage.  


Discussion / hints:
I agree, it is important. 
I've started working on wasm compilation (mving cpp to rust).
and and and... i cannot finish it without tests... so I can add some tests to it.

i recommend test it on rust lvl. it is important for my next PR x]
I have concern 
`fn calc_delta(&self) -> Result<f32, String>` ->
`pub fn calc_nd1nd2(inputs: &Inputs) -> Result<(f32, f32), String> {` ->
`pub fn calc_d1d2(inputs: &Inputs) -> Result<(f32, f32), String> {`
to be precise:
`let numd1 = (inputs.s / inputs.k).ln() + (inputs.r - inputs.q + (sigma.powi(2)) / 2.0) * inputs.t;`

for strike price `inputs.k` = 0.0 we get result from `calc_nd1nd2` equals `+inf, +inf`. 
Implementation of `normal.cdf` correctly returns `1`. 
So in my opinion it shadows error for us.

links:
- https://github.com/hayden4r4/blackscholes-rust/blob/25a040f8d423cac2802f56ebbb77723fad6bbb9a/src/greeks.rs#L41

In my opinion we should return Err for these case. how do you think?

----
UPDATE:
These error is not only here, bcs it is common function ... 
In stochastic tests I get an issue

```

        #[test]
        fn test_implied_volatility_random(f in 50.0_f64..150.0_f64, k in 50.0_f64..150.0_f64, price in 0.01_f64..100.0_f64, t in 0.1_f64..2.0_f64, q in any::<u8>().prop_map(|x| if x % 2 == 0 { OptionType::Call } else { OptionType::Put })) {
            let iv = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);

            let a = f64::total_cmp(&iv, &0.0);
            println!("Comparison result: {:?}", a);
            println!("Random Test - Implied Volatility: {:?}, f {:?}, k {:?} price {:?} t {:?} q {:?}", iv, f, k, price, t, q);
            assert!(!iv.is_sign_negative(), "Implied Volatility is negative");
            assert!(iv >= 0.0, "Implied Volatility is {}", iv);
        }
```

with parameters like:
```
Comparison result: Less
Random Test - Implied Volatility: -1.7976931348623157e308, f 145.23533118406826, k 50.0 price 0.01 t 0.1 q Call
thread 'lets_be_rational::tests::test_implied_volatility_random' panicked at src/lets_be_rational.rs:290:13:
Implied Volatility is negative
```

Based on my knowledge IV cannot be negative. 
Probably algorithm in cpp has a bug in numeric calculation, somewhere we should round to zero or we got underflow unchecked...

Suggestions?

Fix patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index d54fa2f..514bcce 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -18,5 +18,9 @@ jobs:
     - uses: actions/checkout@v3
     - name: Build
       run: cargo build --verbose
+    - name: Cargo fmt
+      run: cargo fmt --all -- --check
+    - name: Cargo clippy
+      run: cargo clippy --verbose
     - name: Run tests
       run: cargo test --verbose
diff --git a/Cargo.lock b/Cargo.lock
index 59e0764..f801f9d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -29,6 +29,21 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
+[[package]]
+name = "bit-set"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
 [[package]]
 name = "bitflags"
 version = "2.4.0"
@@ -39,10 +54,13 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
 name = "blackscholes"
 version = "0.24.0"
 dependencies = [
+ "approx",
  "cc",
  "criterion",
  "libc",
+ "log",
  "num-traits",
+ "proptest",
  "statrs",
 ]
 
@@ -218,24 +236,25 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
 [[package]]
 name = "errno"
-version = "0.3.3"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
 dependencies = [
- "errno-dragonfly",
  "libc",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
+name = "fastrand"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
-dependencies = [
- "cc",
- "libc",
-]
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "getrandom"
@@ -277,7 +296,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
 dependencies = [
  "hermit-abi 0.3.3",
  "rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -321,9 +340,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.148"
+version = "0.2.155"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
 
 [[package]]
 name = "libm"
@@ -333,18 +352,15 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.7"
+version = "0.4.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
 name = "log"
-version = "0.4.17"
+version = "0.4.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
 
 [[package]]
 name = "matrixmultiply"
@@ -366,9 +382,9 @@ dependencies = [
 
 [[package]]
 name = "nalgebra"
-version = "0.29.0"
+version = "0.32.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d506eb7e08d6329505faa8a3a00a5dcc6de9f76e0c77e4b75763ae3c770831ff"
+checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4"
 dependencies = [
  "approx",
  "matrixmultiply",
@@ -384,13 +400,13 @@ dependencies = [
 
 [[package]]
 name = "nalgebra-macros"
-version = "0.1.0"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218"
+checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.68",
 ]
 
 [[package]]
@@ -497,18 +513,44 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.49"
+version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
 dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "proptest"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags",
+ "lazy_static",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax 0.8.4",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
 [[package]]
 name = "quote"
-version = "1.0.23"
+version = "1.0.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 dependencies = [
  "proc-macro2",
 ]
@@ -553,6 +595,15 @@ dependencies = [
  "rand",
 ]
 
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
 [[package]]
 name = "rawpointer"
 version = "0.2.1"
@@ -587,7 +638,7 @@ version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
 dependencies = [
- "regex-syntax",
+ "regex-syntax 0.6.28",
 ]
 
 [[package]]
@@ -596,17 +647,35 @@ version = "0.6.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
 
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
 [[package]]
 name = "rustix"
-version = "0.38.14"
+version = "0.38.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
 dependencies = [
  "bitflags",
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
 ]
 
 [[package]]
@@ -656,7 +725,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
 ]
 
 [[package]]
@@ -672,9 +741,9 @@ dependencies = [
 
 [[package]]
 name = "simba"
-version = "0.6.0"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0b7840f121a46d63066ee7a99fc81dcabbc6105e437cae43528cea199b5a05f"
+checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae"
 dependencies = [
  "approx",
  "num-complex",
@@ -685,12 +754,11 @@ dependencies = [
 
 [[package]]
 name = "statrs"
-version = "0.16.0"
+version = "0.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d08e5e1748192713cc281da8b16924fb46be7b0c2431854eadc785823e5696e"
+checksum = "f697a07e4606a0a25c044de247e583a330dbb1731d11bc7350b81f48ad567255"
 dependencies = [
  "approx",
- "lazy_static",
  "nalgebra",
  "num-traits",
  "rand",
@@ -707,6 +775,29 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "syn"
+version = "2.0.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "tinytemplate"
 version = "1.2.1"
@@ -723,12 +814,27 @@ version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
 
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
 
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "walkdir"
 version = "2.3.2"
@@ -767,7 +873,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
  "wasm-bindgen-shared",
 ]
 
@@ -789,7 +895,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.107",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -857,7 +963,16 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.5",
 ]
 
 [[package]]
@@ -866,13 +981,29 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
 ]
 
 [[package]]
@@ -881,38 +1012,86 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
diff --git a/Cargo.toml b/Cargo.toml
index fa03eb0..55cfeab 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,8 +19,11 @@ cc = { version="1.0", features=["parallel"] }
 
 [dev-dependencies]
 criterion = "0.5"
+proptest = "1.5.0"
+approx = "0.5.1"
 
 [dependencies]
 num-traits = "0.2"
-statrs = "0.16"
+statrs = "0.17.1"
 libc = "0.2"
+log = "0.4.22"
diff --git a/src/greeks.rs b/src/greeks.rs
index 179e366..b24d410 100644
--- a/src/greeks.rs
+++ b/src/greeks.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
 
 use num_traits::Float;
 
-use crate::{*, Inputs, OptionType, Pricing};
+use crate::{Inputs, OptionType, Pricing, *};
 
 pub trait Greeks<T>: Pricing<T>
 where
@@ -41,11 +41,11 @@ impl Greeks<f32> for Inputs {
     /// let delta = inputs.calc_delta().unwrap();
     /// ```
     fn calc_delta(&self) -> Result<f32, String> {
-        let (nd1, _): (f32, f32) = calc_nd1nd2(&self)?;
-        let delta: f32 = match self.option_type {
-            OptionType::Call => nd1 * E.powf(-self.q * self.t),
-            OptionType::Put => -nd1 * E.powf(-self.q * self.t),
-        };
+        let (nd1, _): (f32, f32) = calc_nd1nd2(self)?;
+
+        let option_type: f32 = self.option_type.into();
+        let delta = option_type * E.powf(-self.q * self.t) * nd1;
+
         Ok(delta)
     }
 
@@ -65,7 +65,7 @@ impl Greeks<f32> for Inputs {
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
 
-        let nprimed1: f32 = calc_nprimed1(&self)?;
+        let nprimed1: f32 = calc_nprimed1(self)?;
         let gamma: f32 = E.powf(-self.q * self.t) * nprimed1 / (self.s * sigma * self.t.sqrt());
         Ok(gamma)
     }
@@ -87,24 +87,17 @@ impl Greeks<f32> for Inputs {
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
 
-        let nprimed1: f32 = calc_nprimed1(&self)?;
-        let (nd1, nd2): (f32, f32) = calc_nd1nd2(&self)?;
+        let nprimed1: f32 = calc_nprimed1(self)?;
+        let (nd1, nd2): (f32, f32) = calc_nd1nd2(self)?;
 
         // Calculation uses 365.25 for f32: Time of days per year.
-        let theta: f32 = match self.option_type {
-            OptionType::Call => {
-                (-(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
-                    + self.q * self.s * E.powf(-self.q * self.t) * nd1)
-                    / DAYS_PER_YEAR
-            }
-            OptionType::Put => {
-                (-(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
-                    - self.q * self.s * E.powf(-self.q * self.t) * nd1)
-                    / DAYS_PER_YEAR
-            }
-        };
+        let option_type: f32 = 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
+            + self.q * self.s * E.powf(-self.q * self.t) * nd1 * option_type)
+            / DAYS_PER_YEAR;
+
         Ok(theta)
     }
 
@@ -120,7 +113,7 @@ impl Greeks<f32> for Inputs {
     /// let vega = inputs.calc_vega().unwrap();
     /// ```
     fn calc_vega(&self) -> Result<f32, String> {
-        let nprimed1: f32 = calc_nprimed1(&self)?;
+        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)
     }
@@ -137,11 +130,11 @@ impl Greeks<f32> for Inputs {
     /// let rho = inputs.calc_rho().unwrap();
     /// ```
     fn calc_rho(&self) -> Result<f32, String> {
-        let (_, nd2): (f32, f32) = calc_nd1nd2(&self)?;
-        let rho: f32 = match &self.option_type {
-            OptionType::Call => 1.0 / 100.0 * self.k * self.t * E.powf(-self.r * self.t) * nd2,
-            OptionType::Put => -1.0 / 100.0 * self.k * self.t * E.powf(-self.r * self.t) * nd2,
-        };
+        let (_, nd2): (f32, f32) = calc_nd1nd2(self)?;
+
+        let option_type: f32 = self.option_type.into();
+        let rho = option_type / 100.0 * self.k * self.t * E.powf(-self.r * self.t) * nd2;
+
         Ok(rho)
     }
 
@@ -163,12 +156,12 @@ impl Greeks<f32> for Inputs {
     /// let epsilon = inputs.calc_epsilon().unwrap();
     /// ```
     fn calc_epsilon(&self) -> Result<f32, String> {
-        let (nd1, _) = calc_nd1nd2(&self)?;
+        let (nd1, _) = calc_nd1nd2(self)?;
         let e_negqt = E.powf(-self.q * self.t);
-        let epsilon: f32 = match &self.option_type {
-            OptionType::Call => -self.s * self.t * e_negqt * nd1,
-            OptionType::Put => self.s * self.t * e_negqt * nd1,
-        };
+
+        let option_type: f32 = self.option_type.into();
+        let epsilon: f32 = -self.s * self.t * e_negqt * nd1 * option_type;
+
         Ok(epsilon)
     }
 
@@ -204,8 +197,8 @@ impl Greeks<f32> for Inputs {
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
 
-        let nprimed1 = calc_nprimed1(&self)?;
-        let (_, d2) = calc_d1d2(&self)?;
+        let nprimed1 = calc_nprimed1(self)?;
+        let (_, d2) = calc_d1d2(self)?;
         let vanna: f32 = d2 * E.powf(-self.q * self.t) * nprimed1 * -0.01 / sigma;
         Ok(vanna)
     }
@@ -225,27 +218,16 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .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 nprimed1 = calc_nprimed1(self)?;
+        let (nd1, _) = calc_nd1nd2(self)?;
+        let (_, d2) = calc_d1d2(self)?;
         let e_negqt = E.powf(-self.q * self.t);
 
-        let charm = match &self.option_type {
-            OptionType::Call => {
-                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())
-            }
-            OptionType::Put => {
-                -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())
-            }
-        };
+        let option_type: f32 = self.option_type.into();
+        let charm: f32 = option_type * 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)
     }
 
@@ -264,8 +246,8 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
-        let nprimed1 = calc_nprimed1(&self)?;
-        let (d1, d2) = calc_d1d2(&self)?;
+        let nprimed1 = calc_nprimed1(self)?;
+        let (d1, d2) = calc_d1d2(self)?;
         let e_negqt = E.powf(-self.q * self.t);
 
         let veta = -self.s
@@ -273,7 +255,7 @@ impl Greeks<f32> for Inputs {
             * nprimed1
             * self.t.sqrt()
             * (self.q + ((self.r - self.q) * d1) / (sigma * self.t.sqrt())
-            - ((1.0 + d1 * d2) / (2.0 * self.t)));
+                - ((1.0 + d1 * d2) / (2.0 * self.t)));
         Ok(veta)
     }
 
@@ -292,9 +274,9 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
-        let (d1, d2) = calc_d1d2(&self)?;
+        let (d1, d2) = calc_d1d2(self)?;
 
-        let vomma = Inputs::calc_vega(&self)? * ((d1 * d2) / sigma);
+        let vomma = Inputs::calc_vega(self)? * ((d1 * d2) / sigma);
         Ok(vomma)
     }
 
@@ -313,8 +295,8 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
-        let (d1, _) = calc_d1d2(&self)?;
-        let gamma = Inputs::calc_gamma(&self)?;
+        let (d1, _) = calc_d1d2(self)?;
+        let gamma = Inputs::calc_gamma(self)?;
 
         let speed = -gamma / self.s * (d1 / (sigma * self.t.sqrt()) + 1.0);
         Ok(speed)
@@ -335,8 +317,8 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
-        let (d1, d2) = calc_d1d2(&self)?;
-        let gamma = Inputs::calc_gamma(&self)?;
+        let (d1, d2) = calc_d1d2(self)?;
+        let gamma = Inputs::calc_gamma(self)?;
 
         let zomma = gamma * ((d1 * d2 - 1.0) / sigma);
         Ok(zomma)
@@ -357,17 +339,17 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
-        let (d1, d2) = calc_d1d2(&self)?;
-        let nprimed1 = calc_nprimed1(&self)?;
+        let (d1, d2) = calc_d1d2(self)?;
+        let nprimed1 = calc_nprimed1(self)?;
         let e_negqt = E.powf(-self.q * self.t);
 
         let color = -e_negqt
             * (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);
+                + 1.0
+                + (2.0 * (self.r - self.q) * self.t - d2 * sigma * self.t.sqrt())
+                    / (sigma * self.t.sqrt())
+                    * d1);
         Ok(color)
     }
 
@@ -386,8 +368,8 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
-        let (d1, d2) = calc_d1d2(&self)?;
-        let vega = Inputs::calc_vega(&self)?;
+        let (d1, d2) = calc_d1d2(self)?;
+        let vega = Inputs::calc_vega(self)?;
 
         let ultima =
             -vega / sigma.powf(2.0) * (d1 * d2 * (1.0 - d1 * d2) + d1.powf(2.0) + d2.powf(2.0));
@@ -406,7 +388,7 @@ impl Greeks<f32> for Inputs {
     /// let dual_delta = inputs.calc_dual_delta().unwrap();
     /// ```
     fn calc_dual_delta(&self) -> Result<f32, String> {
-        let (_, nd2) = calc_nd1nd2(&self)?;
+        let (_, nd2) = calc_nd1nd2(self)?;
         let e_negqt = E.powf(-self.q * self.t);
 
         let dual_delta = match self.option_type {
@@ -431,7 +413,7 @@ impl Greeks<f32> for Inputs {
         let sigma = self
             .sigma
             .ok_or("Expected Some(f32) for self.sigma, received None")?;
-        let nprimed2 = calc_nprimed2(&self)?;
+        let nprimed2 = calc_nprimed2(self)?;
         let e_negqt = E.powf(-self.q * self.t);
 
         let dual_gamma = e_negqt * (nprimed2 / (self.k * sigma * self.t.sqrt()));
diff --git a/src/implied_volatility.rs b/src/implied_volatility.rs
index ee6e969..c5c217c 100644
--- a/src/implied_volatility.rs
+++ b/src/implied_volatility.rs
@@ -1,6 +1,9 @@
 use num_traits::Float;
 
-use crate::{*, greeks::Greeks, Inputs, lets_be_rational::implied_volatility_from_a_transformed_rational_guess, OptionType, pricing::Pricing};
+use crate::{
+    greeks::Greeks, lets_be_rational::implied_volatility_from_a_transformed_rational_guess,
+    pricing::Pricing, Inputs, *,
+};
 
 pub trait ImpliedVolatility<T>: Pricing<T> + Greeks<T>
 where
@@ -110,13 +113,13 @@ impl ImpliedVolatility<f32> for Inputs {
         // The Black-Scholes-Merton formula takes into account dividend yield by setting S = S * e^{-qt}, do this here with the forward
         let f = f * (-self.q * self.t).exp();
 
-        // convert the option type into \theta
-        let q: f64 = match self.option_type {
-            OptionType::Call => 1.0,
-            OptionType::Put => -1.0,
-        };
-
-        let sigma = implied_volatility_from_a_transformed_rational_guess(p as f64, f as f64, self.k as f64, self.t as f64, q as f64);
+        let sigma = implied_volatility_from_a_transformed_rational_guess(
+            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 < 0.0 {
             Err("Implied volatility failed to converge".to_string())?
diff --git a/src/inputs.rs b/src/inputs.rs
index 5100a1a..857ac95 100644
--- a/src/inputs.rs
+++ b/src/inputs.rs
@@ -1,7 +1,8 @@
+use libc::c_double;
 use std::fmt::{Display, Formatter, Result as fmtResult};
 
 /// The type of option to be priced (call or put).
-#[derive(Debug, Clone, Eq, PartialEq)]
+#[derive(Debug, Clone, Eq, PartialEq, Copy)]
 pub enum OptionType {
     Call,
     Put,
@@ -16,6 +17,24 @@ impl Display for OptionType {
     }
 }
 
+impl From<OptionType> for f32 {
+    fn from(val: OptionType) -> Self {
+        match val {
+            OptionType::Call => 1.0,
+            OptionType::Put => -1.0,
+        }
+    }
+}
+
+impl From<OptionType> for c_double {
+    fn from(val: OptionType) -> Self {
+        match val {
+            OptionType::Call => 1.0,
+            OptionType::Put => -1.0,
+        }
+    }
+}
+
 /// The inputs to the Black-Scholes-Merton model.
 #[derive(Debug, Clone, PartialEq)]
 pub struct Inputs {
@@ -56,6 +75,7 @@ impl Inputs {
     /// ```
     /// # Returns
     /// An instance of the `Inputs` struct.
+    #[allow(clippy::too_many_arguments)]
     pub fn new(
         option_type: OptionType,
         s: f32,
diff --git a/src/lets_be_rational.rs b/src/lets_be_rational.rs
index 716852e..a357f8f 100644
--- a/src/lets_be_rational.rs
+++ b/src/lets_be_rational.rs
@@ -1,5 +1,8 @@
 /// FFI wrapper for the `lets_be_rational` library.
 use libc::c_double;
+use log::error;
+
+use crate::OptionType;
 
 #[link(name = "liblets_be_rational")]
 extern "C" {
@@ -16,13 +19,7 @@ extern "C" {
 
     #[link_name = "black"]
     //  double K, double sigma, double T, double q /* q=±1 */) -> c_double
-    fn black_ffi(
-        F: c_double,
-        K: c_double,
-        sigma: c_double,
-        T: c_double,
-        q: c_double,
-    ) -> c_double;
+    fn black_ffi(F: c_double, K: c_double, sigma: c_double, T: c_double, q: c_double) -> c_double;
 }
 
 /// This function returns the implied volatility of an option contract using a transformed rational approximation.
@@ -37,14 +34,29 @@ pub fn implied_volatility_from_a_transformed_rational_guess(
     f: f64,
     k: f64,
     t: f64,
-    q: f64,
+    q: OptionType,
 ) -> f64 {
-    let price: c_double = price.into();
-    let f: c_double = f.into();
-    let k: c_double = k.into();
-    let t: c_double = t.into();
-    let q: c_double = q.into();
-    unsafe { implied_volatility_from_a_transformed_rational_guess_ffi(price, f, k, t, q) }
+    let price: c_double = price;
+    let f: c_double = f;
+    let k: c_double = k;
+    let t: c_double = t;
+    let q: c_double = match q {
+        OptionType::Call => 1.0,
+        OptionType::Put => -1.0,
+    };
+    unsafe {
+        let result = implied_volatility_from_a_transformed_rational_guess_ffi(price, f, k, t, q);
+
+        if result.is_nan() {
+            error!("Implied volatility has failed in the calculation - NaN");
+            0.0
+        } else if result.is_sign_negative() {
+            error!("Implied volatility has failed in the calculation - Negative value");
+            result
+        } else {
+            result
+        }
+    }
 }
 
 /// This function returns the Black price of an option contract.
@@ -53,11 +65,272 @@ pub fn implied_volatility_from_a_transformed_rational_guess(
 /// f, k, sigma, t, q.
 /// # Returns
 /// f64 of the price of the option.
-pub fn black(f: f64, k: f64, sigma: f64, t: f64, q: f64) -> f64 {
-    let f: c_double = f.into();
-    let k: c_double = k.into();
-    let sigma: c_double = sigma.into();
-    let t: c_double = t.into();
-    let q: c_double = q.into();
-    unsafe { black_ffi(f, k, sigma, t, q) }
+pub fn black(f: f64, k: f64, sigma: f64, t: f64, q: OptionType) -> f64 {
+    let f: c_double = f;
+    let k: c_double = k;
+    let sigma: c_double = sigma;
+    let t: c_double = t;
+
+    unsafe { black_ffi(f, k, sigma, t, q.into()) }
+}
+
+#[cfg(test)]
+mod tests {
+    use proptest::prelude::*;
+
+    use super::*;
+
+    #[test]
+    fn test_black() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let sigma = 0.2;
+        let t = 1.0;
+        let q = OptionType::Call;
+
+        let expected_result = 7.965_567_455_405_798;
+
+        // act
+        let result = black(f, k, sigma, t, q);
+
+        // assert
+        assert!(prop_approx_eq(result, expected_result, 1e-13));
+    }
+
+    #[test]
+    fn test_black_zero_values() {
+        // arrange
+        let f = 0.0;
+        let k = 0.0;
+        let sigma = 0.0;
+        let t = 0.0;
+        let q = OptionType::Call;
+        let expected_result = 0.0;
+
+        // act
+        let result = black(f, k, sigma, t, q);
+
+        // assert
+        assert_eq!(result, expected_result);
+    }
+
+    #[test]
+    fn test_black_negative_values() {
+        // arrange
+        let f = -100.0;
+        let k = -100.0;
+        let sigma = -0.2;
+        let t = -1.0;
+        let q = OptionType::Put;
+        let expected_result = 0.0;
+
+        // act
+        let result = black(f, k, sigma, t, q);
+
+        // assert
+        assert_eq!(result, expected_result);
+    }
+
+    #[test]
+    fn test_black_high_sigma() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let sigma = 100.0;
+        let t = 1.0;
+        let q = OptionType::Call;
+        let expected_result = 0.0;
+
+        // act
+        let result = black(f, k, sigma, t, q);
+
+        // assert
+        assert_eq!(result, expected_result);
+    }
+
+    #[test]
+    fn test_black_high_t() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let sigma = 0.2;
+        let t = 100.0;
+        let q = OptionType::Call;
+
+        // act
+        let result = black(f, k, sigma, t, q);
+
+        let expected_result = 68.26894921370861;
+
+        // assert
+        assert!(prop_approx_eq(result, expected_result, 1e-13));
+    }
+
+    fn prop_approx_eq(a: f64, b: f64, epsilon: f64) -> bool {
+        (a - b).abs() < epsilon
+    }
+
+    #[test]
+    fn test_black_function() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let sigma = 0.2;
+        let t = 1.0;
+        let q = OptionType::Call;
+
+        // act
+        let price = black(f, k, sigma, t, q);
+
+        // assert
+        assert!(prop_approx_eq(price, 7.965567455405804, 1e-9));
+    }
+
+    #[test]
+    fn test_implied_volatility() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let price = 7.965567455405804;
+        let t = 1.0;
+        let q = OptionType::Put;
+
+        // act
+        let sigma = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);
+
+        // assert
+        assert!(prop_approx_eq(sigma, 0.2, 1e-9));
+    }
+
+    #[test]
+    fn test_black_function_typical_values() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let sigma = 0.2;
+        let t = 1.0;
+        let q = OptionType::Call;
+
+        // act
+        let price = black(f, k, sigma, t, q);
+
+        // assert
+        assert!(prop_approx_eq(price, 7.965567455405804, 1e-9));
+    }
+
+    #[test]
+    fn test_black_function_edge_case_high_volatility() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let sigma = 5.0;
+        let t = 1.0;
+        let q = OptionType::Put;
+
+        // act
+        let price = black(f, k, sigma, t, q);
+
+        // assert
+        assert!(price > 0.0);
+    }
+
+    #[test]
+    fn test_black_function_edge_case_zero_time() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let sigma = 0.2;
+        let t = 0.0;
+        let q = OptionType::Put;
+
+        // act
+        let price = black(f, k, sigma, t, q);
+
+        // assert
+        assert_eq!(price, 0.0);
+    }
+
+    #[test]
+    fn test_implied_volatility_typical_values() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let price = 7.965567455405804;
+        let t = 1.0;
+        let q = OptionType::Call;
+
+        // act
+        let result = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);
+
+        // assert
+        assert!(prop_approx_eq(result, 0.2, 1e-9));
+    }
+
+    #[test]
+    fn test_implied_volatility_edge_case_high_price() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let price = 50.0;
+        let t = 1.0;
+        let q = OptionType::Put;
+
+        // act
+        let result = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);
+
+        // assert
+        assert!(result > 0.0);
+    }
+
+    #[test]
+    fn test_implied_volatility_edge_case_zero_price() {
+        // arrange
+        let f = 100.0;
+        let k = 100.0;
+        let price = 0.01;
+        let t = 1.0;
+        let q = OptionType::Call;
+
+        // act
+        let result = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);
+
+        // assert
+        assert!(result > 0.0);
+    }
+
+    proptest! {
+        #[test]
+        fn test_black_function_random(f in 50.0_f64..150.0_f64, k in 50.0_f64..150.0_f64, sigma in 0.01_f64..2.0_f64, t in 0.1_f64..2.0_f64, q in any::<u8>().prop_map(|x| if x % 2 == 0 { OptionType::Call } else { OptionType::Put })) {
+            let price = black(f, k, sigma, t, q);
+            assert!(price >= 0.0);
+        }
+
+        // TODO: to discuss how to fix this test. Correspond to issue #8
+        // #[test]
+        fn test_implied_volatility_random(f in 50.0_f64..150.0_f64, k in 50.0_f64..150.0_f64, price in 0.01_f64..100.0_f64, t in 0.1_f64..2.0_f64, q in any::<u8>().prop_map(|x| if x % 2 == 0 { OptionType::Call } else { OptionType::Put })) {
+            let iv = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);
+
+            let a = f64::total_cmp(&iv, &0.0);
+            println!("Comparison result: {:?}", a);
+            println!("Random Test - Implied Volatility: {:?}, f {:?}, k {:?} price {:?} t {:?} q {:?}", iv, f, k, price, t, q);
+            assert!(!iv.is_sign_negative(), "Implied Volatility is negative");
+            assert!(iv >= 0.0, "Implied Volatility is {}", iv);
+        }
+    }
+
+    // TODO: to discuss how to fix this test. Correspond to issue #8
+    // #[test]
+    #[allow(dead_code)]
+    fn test_implied_volatility_specific() {
+        let f = 137.9331040666909;
+        let k = 50.0;
+        let price = 0.01;
+        let t = 0.1;
+        let q = OptionType::Call;
+
+        let result = implied_volatility_from_a_transformed_rational_guess(price, f, k, t, q);
+
+        assert!(!result.is_sign_negative());
+    }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 4361da8..de80c70 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,8 +29,8 @@ pub use pricing::Pricing;
 mod greeks;
 mod implied_volatility;
 mod inputs;
-mod pricing;
 pub mod lets_be_rational;
+mod pricing;
 
 pub(crate) const N_MEAN: f32 = 0.0;
 pub(crate) const N_STD_DEV: f32 = 1.0;
@@ -38,13 +38,12 @@ 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: f32 = 4.62627532e-01;
-pub(crate) const B: f32 = -1.16851917e-02;
-pub(crate) const C: f32 = 9.63541838e-04;
-pub(crate) const D: f32 = 7.53502261e-05;
-pub(crate) const _E: f32 = 1.42451646e-05;
-pub(crate) const F: f32 = -2.10237683e-05;
-
+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
@@ -56,10 +55,20 @@ pub(crate) fn calc_d1d2(inputs: &Inputs) -> Result<(f32, f32), String> {
         .sigma
         .ok_or("Expected Some(f32) for self.sigma, received None")?;
     // Calculating numerator of d1
-    let numd1 =
-        (inputs.s / inputs.k).ln() + (inputs.r - inputs.q + (sigma.powi(2)) / 2.0) * inputs.t;
+    let part1 = (inputs.s / inputs.k).ln();
+
+    if part1.is_infinite() {
+        return Err("Log from s/k is infinity".to_string());
+    }
+
+    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 == 0.0 {
+        return Err("Time to maturity is 0".to_string());
+    }
+
     let den = sigma * (inputs.t.sqrt());
 
     let d1 = numd1 / den;
@@ -111,7 +120,7 @@ pub(crate) fn calc_npdf(x: f32) -> f32 {
 /// # Returns
 /// f32 of the derivative of the nd1.
 pub fn calc_nprimed1(inputs: &Inputs) -> Result<f32, String> {
-    let (d1, _) = calc_d1d2(&inputs)?;
+    let (d1, _) = calc_d1d2(inputs)?;
 
     // Get the standard n probability density function value of d1
     let nprimed1 = calc_npdf(d1);
@@ -121,9 +130,9 @@ pub fn calc_nprimed1(inputs: &Inputs) -> Result<f32, String> {
 /// # Returns
 /// f32 of the derivative of the nd2.
 pub(crate) fn calc_nprimed2(inputs: &Inputs) -> Result<f32, String> {
-    let (_, d2) = calc_d1d2(&inputs)?;
+    let (_, d2) = calc_d1d2(inputs)?;
 
     // Get the standard n probability density function value of d1
     let nprimed2 = calc_npdf(d2);
     Ok(nprimed2)
-}
\ No newline at end of file
+}
diff --git a/src/pricing.rs b/src/pricing.rs
index 914b3c6..a4707df 100644
--- a/src/pricing.rs
+++ b/src/pricing.rs
@@ -2,7 +2,7 @@ use std::f32::consts::E;
 
 use num_traits::Float;
 
-use crate::{*, Inputs, lets_be_rational, OptionType};
+use crate::{lets_be_rational, Inputs, OptionType, *};
 
 pub trait Pricing<T>
 where
@@ -26,7 +26,7 @@ impl Pricing<f32> for Inputs {
     /// ```
     fn calc_price(&self) -> Result<f32, String> {
         // Calculates the price of the option
-        let (nd1, nd2): (f32, f32) = calc_nd1nd2(&self)?;
+        let (nd1, nd2): (f32, f32) = calc_nd1nd2(self)?;
         let price: f32 = match self.option_type {
             OptionType::Call => f32::max(
                 0.0,
@@ -59,19 +59,13 @@ impl Pricing<f32> for Inputs {
         // let's be rational wants the forward price, not the spot price.
         let forward = self.s * ((self.r - self.q) * self.t).exp();
 
-        // convert the option type into \theta
-        let q: f64 = match self.option_type {
-            OptionType::Call => 1.0,
-            OptionType::Put => -1.0,
-        };
-
         // price using `black`
         let undiscounted_price = lets_be_rational::black(
             forward as f64,
             self.k as f64,
             sigma as f64,
             self.t as f64,
-            q,
+            self.option_type,
         );
 
         // discount the price


Test patch:
diff --git a/proptest-regressions/lets_be_rational.txt b/proptest-regressions/lets_be_rational.txt
new file mode 100644
index 0000000..f42a1c9
--- /dev/null
+++ b/proptest-regressions/lets_be_rational.txt
@@ -0,0 +1,7 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+cc 916ee409e5242a985e5cf4f8b1a74e06b305ac2558f3b3378e46805c5f2af1a7 # shrinks to f = 50.0, k = 130.55862834828156, price = 0.01, t = 0.1, q = Put
diff --git a/src/greeks_tests.rs b/src/greeks_tests.rs
new file mode 100644
index 0000000..6557963
--- /dev/null
+++ b/src/greeks_tests.rs
@@ -0,0 +1,109 @@
+#[cfg(test)]
+mod tests {
+    use crate::{Greeks, Inputs, OptionType};
+
+    #[test]
+    fn test_calc_delta_zero_stock_price() {
+        // arrange
+        let option_type = OptionType::Call;
+        let s = 0.0; // extreme value
+        let k = 100.0;
+        let p = None;
+        let r = 0.05;
+        let q = 0.0;
+        let sigma = Some(0.2);
+        let t = 20.0 / 365.25;
+
+        let inputs = Inputs::new(option_type, s, k, p, r, q, t, sigma);
+
+        // act
+        let result = inputs.calc_delta();
+
+        // assert
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_calc_delta_zero_strike_price() {
+        // arrange
+        let option_type = OptionType::Call;
+        let s = 100.0;
+        let k = 0.0;
+        let p = None;
+        let r = 0.05;
+        let q = 0.0;
+        let sigma = Some(0.2);
+        let t = 20.0 / 365.25;
+
+        let inputs = Inputs::new(option_type, s, k, p, r, q, t, sigma);
+
+        // act
+        let result = inputs.calc_delta();
+
+        // assert
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_calc_delta_zero_risk_free_rate() {
+        // arrange
+        let option_type = OptionType::Call;
+        let s = 100.0;
+        let k = 100.0;
+        let p = None;
+        let r = 0.0; // extreme value
+        let q = 0.0;
+        let sigma = Some(0.2);
+        let t = 20.0 / 365.25;
+
+        let inputs = Inputs::new(option_type, s, k, p, r, q, t, sigma);
+
+        // act
+        let result = inputs.calc_delta();
+
+        // assert
+        assert!(result.is_ok());
+    }
+
+    #[test]
+    fn test_calc_delta_none_volatility() {
+        // arrange
+        let option_type = OptionType::Call;
+        let s = 100.0;
+        let k = 100.0;
+        let p = None;
+        let r = 0.05;
+        let q = 0.0;
+        let sigma = None; // extreme value
+        let t = 20.0 / 365.25;
+
+        let inputs = Inputs::new(option_type, s, k, p, r, q, t, sigma);
+
+        // act
+        let result = inputs.calc_delta();
+
+        // assert
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_calc_delta_zero_time_to_maturity() {
+        // arrange
+        let option_type = OptionType::Call;
+        let s = 100.0;
+        let k = 100.0;
+        let p = None;
+        let r = 0.05;
+        let q = 0.0;
+        let sigma = Some(0.2);
+        let t = 0.0; // extreme value
+
+        let inputs = Inputs::new(option_type, s, k, p, r, q, t, sigma);
+
+        // act
+        let result = inputs.calc_delta();
+
+        // assert
+        assert!(result.is_err());
+    }
+}
diff --git a/tests/bstest.rs b/tests/bstest.rs
index 473e13f..dc1621a 100644
--- a/tests/bstest.rs
+++ b/tests/bstest.rs
@@ -62,23 +62,27 @@ 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() as f32)
+        (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() as f32)
+        (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() as f32)
+        (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() as f32)
+        (INPUTS_PUT_ITM.calc_price().unwrap()
+            - INPUTS_PUT_ITM.calc_rational_price().unwrap() as f32)
             .abs()
             < 0.001
     );
-}
\ No newline at end of file
+}