parent
00e6a33497
commit
a2ce71161e
52
Cargo.lock
generated
52
Cargo.lock
generated
@ -156,6 +156,31 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "csbindgen"
|
||||
version = "1.9.3"
|
||||
@ -166,6 +191,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@ -193,6 +224,7 @@ name = "finlib"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"pyo3",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -426,6 +458,26 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
|
@ -18,6 +18,9 @@ version = "0.0.1"
|
||||
authors = ["sarsoo <andy@sarsoo.xyz>"]
|
||||
edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
rayon = "1.10.0"
|
||||
|
||||
[workspace.dependencies.pyo3]
|
||||
version = "0.23.4"
|
||||
# "abi3-py38" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.8
|
||||
|
@ -9,8 +9,26 @@ public class Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAdd()
|
||||
public void TestCompoundInterest()
|
||||
{
|
||||
FinLib.Add(10, 10).Should().Be(20);
|
||||
FinLib.CompoundInterest(100, 0.05, 1, 1).Should().Be(105);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCompoundInterestMonthly()
|
||||
{
|
||||
Math.Round(FinLib.CompoundInterest(100, 0.05, 1, 12), 2).Should().Be(105.12);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Covariance()
|
||||
{
|
||||
FinLib.Covariance([1d, 2d, 3d, 4], [1d, 2, 3, 4]).Should().Be(1.6666666666666667);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CovarianceBreaking()
|
||||
{
|
||||
FinLib.Covariance([1d, 2d, 3d, 4], [1d]).Should().BeNull();
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2"/>
|
||||
<PackageReference Include="FluentAssertions" Version="8.0.1" />
|
||||
<PackageReference Include="FluentAssertions" Version="7.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
|
||||
<PackageReference Include="NUnit" Version="4.2.2"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="4.3.0"/>
|
||||
|
@ -1,9 +1,35 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FinLib;
|
||||
|
||||
public static class FinLib
|
||||
{
|
||||
public static ulong Add(ulong a, ulong b)
|
||||
public static double CompoundInterest(double principal, double rate, double time, double n)
|
||||
{
|
||||
return NativeMethods.add(a, b);
|
||||
return NativeMethods.interest_compound(principal, rate, time, n);
|
||||
}
|
||||
|
||||
public static double? Covariance(IEnumerable<double> valuesOne, IEnumerable<double> valuesTwo)
|
||||
{
|
||||
unsafe {
|
||||
var valuesOneArr = valuesOne.ToArray();
|
||||
var valuesTwoArr = valuesTwo.ToArray();
|
||||
fixed (double* ptrOne = valuesOneArr)
|
||||
fixed (double* ptrTwo = valuesTwoArr) {
|
||||
var ret = NativeMethods.covariance(ptrOne, (UIntPtr)valuesOneArr.Length, ptrTwo, (UIntPtr) valuesTwoArr.Length);
|
||||
|
||||
if (ret == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,11 +16,11 @@ namespace FinLib
|
||||
|
||||
|
||||
|
||||
[DllImport(__DllName, EntryPoint = "add", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern ulong add(ulong left, ulong right);
|
||||
|
||||
[DllImport(__DllName, EntryPoint = "interest_compound", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern float interest_compound(float principal, float rate, float time, float n);
|
||||
internal static extern double interest_compound(double principal, double rate, double time, double n);
|
||||
|
||||
[DllImport(__DllName, EntryPoint = "covariance", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
internal static extern double* covariance(double* arr, nuint len, double* arr_two, nuint len_two);
|
||||
|
||||
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ namespace finlib {
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint64_t add(uint64_t left, uint64_t right);
|
||||
const double *covariance(const double *arr, size_t len, const double *arr_two, size_t len_two);
|
||||
|
||||
float interest_compound(float principal, float rate, float time, float n);
|
||||
double interest_compound(double principal, double rate, double time, double n);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
|
@ -1,9 +1,26 @@
|
||||
use std::ptr::null;
|
||||
use std::slice;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn add(left: u64, right: u64) -> u64 {
|
||||
finlib::add(left, right)
|
||||
pub extern "C" fn interest_compound(principal: f64, rate: f64, time: f64, n: f64) -> f64 {
|
||||
finlib::interest::compound(principal, rate, time, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn interest_compound(principal: f32, rate: f32, time: f32, n: f32) -> f32 {
|
||||
finlib::interest::compound(principal, rate, time, n)
|
||||
pub unsafe extern "C" fn covariance(arr: *const f64, len: usize, arr_two: *const f64, len_two: usize) -> *const f64 {
|
||||
let input_array = unsafe {
|
||||
assert!(!arr.is_null());
|
||||
slice::from_raw_parts(arr, len)
|
||||
};
|
||||
|
||||
let input_array_two = unsafe {
|
||||
assert!(!arr_two.is_null());
|
||||
slice::from_raw_parts(arr_two, len_two)
|
||||
};
|
||||
|
||||
match finlib::stats::covariance(input_array, input_array_two) {
|
||||
None => null(),
|
||||
Some(v) => Box::into_raw(Box::new(v))
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
finlib::add(left, right)
|
||||
pub fn compound(principal: f64, rate: f64, time: f64, n: f64) -> f64 {
|
||||
finlib::interest::compound(principal, rate, time, n)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn compound(principal: f32, rate: f32, time: f32, n: f32) -> f32 {
|
||||
finlib::interest::compound(principal, rate, time, n)
|
||||
pub fn covariance(slice: Vec<f64>, slice_two: Vec<f64>) -> Option<f64> {
|
||||
finlib::stats::covariance(&slice, &slice_two)
|
||||
}
|
@ -6,6 +6,7 @@ edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pyo3 = { workspace = true, optional = true }
|
||||
rayon = { workspace = true }
|
||||
|
||||
[features]
|
||||
py = ["dep:pyo3"]
|
@ -1,7 +1,11 @@
|
||||
|
||||
pub fn compound(principal: f32, rate: f32, time: f32, n: f32) -> f32 {
|
||||
pub fn compound_32(principal: f32, rate: f32, time: f32, n: f32) -> f32 {
|
||||
principal * f32::powf( 1f32 + (rate / n), time * n)
|
||||
}
|
||||
|
||||
pub fn compound(principal: f64, rate: f64, time: f64, n: f64) -> f64 {
|
||||
principal * f64::powf( 1f64 + (rate / n), time * n)
|
||||
}
|
||||
/// https://www.thecalculatorsite.com/finance/calculators/compoundinterestcalculator.php
|
||||
|
||||
#[cfg(test)]
|
||||
@ -9,14 +13,26 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn annual_compound() {
|
||||
let result = compound(100f32, 0.05f32, 1f32, 1f32);
|
||||
fn annual_compound_32() {
|
||||
let result = compound_32(100f32, 0.05f32, 1f32, 1f32);
|
||||
assert_eq!(f32::round(result), 105f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monthly_compound() {
|
||||
let result = compound(100f32, 0.05f32, 1f32, 12f32);
|
||||
fn monthly_compound_32() {
|
||||
let result = compound_32(100f32, 0.05f32, 1f32, 12f32);
|
||||
assert_eq!(f32::round(result * 100f32) / 100f32, 105.12f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn annual_compound() {
|
||||
let result = compound(100f64, 0.05f64, 1f64, 1f64);
|
||||
assert_eq!(f64::round(result), 105f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monthly_compound() {
|
||||
let result = compound(100f64, 0.05f64, 1f64, 12f64);
|
||||
assert_eq!(f64::round(result * 100f64) / 100f64, 105.12f64);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,2 @@
|
||||
pub mod interest;
|
||||
|
||||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
pub mod stats;
|
35
finlib/src/stats/covariance.rs
Normal file
35
finlib/src/stats/covariance.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use rayon::prelude::*;
|
||||
use super::mean;
|
||||
|
||||
pub fn covariance(slice: &[f64], slice_two: &[f64]) -> Option<f64>
|
||||
{
|
||||
match slice.len() - slice_two.len() {
|
||||
0 => {
|
||||
let mean_1 = mean(slice);
|
||||
let mean_2 = mean(slice_two);
|
||||
|
||||
Some(slice.par_iter().zip(slice_two.par_iter())
|
||||
.map(|(x, y)| (x - mean_1) * (y - mean_2))
|
||||
.sum::<f64>()
|
||||
/ ((slice.len() - 1) as f64))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn covariance_test() {
|
||||
let result = covariance(&[1f64, 2f64, 3f64, 4f64], &[1f64, 2f64, 3f64, 4f64]);
|
||||
assert_eq!(result.unwrap(), 1.6666666666666667f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn covariance_test_break() {
|
||||
let result = covariance(&[1f64, 2f64, 3f64, 4f64], &[1f64, 2f64]);
|
||||
assert_eq!(result.is_none(), true);
|
||||
}
|
||||
}
|
37
finlib/src/stats/mod.rs
Normal file
37
finlib/src/stats/mod.rs
Normal file
@ -0,0 +1,37 @@
|
||||
mod covariance;
|
||||
pub use covariance::*;
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn mean(slice: &[f64]) -> f64
|
||||
{
|
||||
slice.par_iter().sum::<f64>() / slice.len() as f64
|
||||
}
|
||||
|
||||
pub fn population_variance(slice: &[f64]) -> f64
|
||||
{
|
||||
let mean = mean(slice);
|
||||
slice.par_iter()
|
||||
.map(|x| f64::powi(x - mean, 2))
|
||||
.sum::<f64>()
|
||||
/ slice.len() as f64
|
||||
}
|
||||
|
||||
pub fn sample_variance(slice: &[f64]) -> f64
|
||||
{
|
||||
let mean = mean(slice);
|
||||
slice.par_iter()
|
||||
.map(|x| f64::powi(x - mean, 2))
|
||||
.sum::<f64>()
|
||||
/ ((slice.len() - 1) as f64)
|
||||
}
|
||||
|
||||
pub fn population_std_dev(slice: &[f64]) -> f64
|
||||
{
|
||||
f64::sqrt(population_variance(slice))
|
||||
}
|
||||
|
||||
pub fn sample_std_dev(slice: &[f64]) -> f64
|
||||
{
|
||||
f64::sqrt(sample_variance(slice))
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[pyfunction]
|
||||
fn add(left: u64, right: u64) -> PyResult<u64> {
|
||||
Ok(finlib::add(left, right))
|
||||
pub fn compound(principal: f64, rate: f64, time: f64, n: f64) -> PyResult<f64> {
|
||||
Ok(finlib::interest::compound(principal, rate, time, n))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
pub fn compound(principal: f32, rate: f32, time: f32, n: f32) -> PyResult<f32> {
|
||||
Ok(finlib::interest::compound(principal, rate, time, n))
|
||||
pub fn covariance(slice: Vec<f64>, slice_two: Vec<f64>) -> PyResult<Option<f64>> {
|
||||
Ok(finlib::stats::covariance(&slice, &slice_two))
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
fn pyfinlib(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(add, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(compound, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(covariance, m)?)?;
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user