commit 4d15858e45134337433670068ae98640ecbf0b91 Author: Timothy J. Warren Date: Wed Feb 12 22:29:57 2020 -0500 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef48da1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,141 @@ +# Created by https://www.gitignore.io/api/rust,linux,macos,jetbrains+all +# Edit at https://www.gitignore.io/?templates=rust,linux,macos,jetbrains+all + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +/target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# End of https://www.gitignore.io/api/rust,linux,macos,jetbrains+all diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..697e6be --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "rusty-numbers" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4c5ab17 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rusty-numbers" +version = "0.1.0" +authors = ["Timothy J. Warren "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/bigint.rs b/src/bigint.rs new file mode 100644 index 0000000..03f2bf5 --- /dev/null +++ b/src/bigint.rs @@ -0,0 +1,58 @@ +//! Arbitrarily large integers +use crate::Unsigned; + +#[derive(Debug)] +enum BigIntSign { + Positive, + Negative +} + +#[derive(Debug)] +pub struct BigInt { + inner: Vec, + sign: BigIntSign, +} + +impl Default for BigInt { + fn default() -> Self { + Self { + inner: vec![], + sign: BigIntSign::Positive, + } + } +} + +impl From for BigInt { + fn from(n: T) -> Self { + let mut new = Self::default(); + + if n > usize::max_value() { + new.inner = Self.split(n); + } + + new + } +} + +impl BigInt { + /// Split an unsigned number into BigInt parts + fn split(num: T) -> Vec { + // Pretty easy if you don't actually need to split the value! + if num < usize::max_value() { + return vec![T::into()]; + } + + todo!(); + } +} + +impl BigInt { + pub fn new() -> Self { + Self::default() + } +} + +#[cfg(test)] +mod tests { + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3fb4e3c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,23 @@ +#![forbid(unsafe_code)] + +pub mod bigint; +pub mod rational; +pub mod seq; + +/// Dummy trait for implementing generics on unsigned number types +pub trait Unsigned {} + +impl Unsigned for u8 {} +impl Unsigned for u16 {} +impl Unsigned for u32 {} +impl Unsigned for u64 {} +impl Unsigned for usize {} +impl Unsigned for u128 {} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/src/rational.rs b/src/rational.rs new file mode 100644 index 0000000..5e735a4 --- /dev/null +++ b/src/rational.rs @@ -0,0 +1,26 @@ +use crate::Unsigned; + +pub enum FracType { + Proper(T, Frac), + Improper(Frac), +} + +pub struct Frac { + numer: T, + denom: T, +} + +impl Frac { + /// Create a new rational number + pub fn new(n: T, d: T) -> Self { + Frac { + numer: n, + denom: d, + } + } +} + +#[cfg(test)] +mod tests { + +} \ No newline at end of file diff --git a/src/seq.rs b/src/seq.rs new file mode 100644 index 0000000..85cf0a7 --- /dev/null +++ b/src/seq.rs @@ -0,0 +1,93 @@ +///! Sequences and other 'stock' math functions + +///! Calculate a number in the fibonacci sequence, +///! using a lookup table for better worst-case performance. +/// +/// Can calculate up to 186 using native unsigned 128 bit integers. +pub fn fibonacci(n: usize) -> Option { + let mut table: Vec = vec![]; + + _fibonacci(n, &mut table) +} + +/// Actual calculating function for `fibonacci` +#[inline] +fn _fibonacci(n: usize, table: &mut Vec) -> Option { + match table.get(n) { + Some(x) => Some(*x), + None => { + let a = _fibonacci(n - 1, table)?; + let b = _fibonacci(n - 2, table)?; + + // Check for overflow when adding + let attempt = a.checked_add(b); + + if let Some(current) = attempt { + table.insert(n, current); + } + + attempt + } + } +} + +///! Calculate the value of a factorial, +///! using a lookup table for better worst-case performance. +/// +/// Can calculate up to 34! using native unsigned 128 bit integers. +/// If the result overflows, an error message will be displayed. +pub fn factorial(n: usize) -> Option { + let mut table: Vec = vec![0, 1, 1, 2, 3, 5]; + + _factorial(n, &mut table) +} + +/// Actual Calculation function for factoral +#[inline] +fn _factorial (n: usize, table: &mut Vec) -> Option { + match table.get(n) { + // Vec.get returns a Option with a reference to the value, + // so deref and wrap in Some() for proper return type + Some(x) => Some(*x), + None => { + // The ? suffix passes along the Option value + // to be handled later + // See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator + let prev = _factorial(n - 1, table)?; + + // Do an overflow-checked multiply + let attempt = (n as u128).checked_mul(prev); + + // If there isn't an overflow, add the result + // to the calculation table + if let Some(current) = attempt { + table.insert(n, current); + } + + attempt // Some(x) if no overflow + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_factorial() { + let res = factorial(34); + assert!(res.is_some()); + + let res = factorial(35); + assert!(res.is_none()); + } + + #[test] + fn test_fibonacci() { + let res = fibonacci(186); + assert!(res.is_some()); + + let res = fibonacci(187); + assert!(res.is_none()); + } +}