diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..531ddd1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,55 @@
+name: CI
+
+on: [push, pull_request]
+
+jobs:
+ ci:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ rust-toolchain: [nightly]
+ targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ with:
+ toolchain: ${{ matrix.rust-toolchain }}
+ components: rust-src, clippy, rustfmt
+ targets: ${{ matrix.targets }}
+ - name: Check rust version
+ run: rustc --version --verbose
+ - name: Check code format
+ run: cargo fmt --all -- --check
+ - name: Clippy
+ run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
+ - name: Build
+ run: cargo build --target ${{ matrix.targets }} --all-features
+ - name: Unit test
+ if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
+ run: cargo test --target ${{ matrix.targets }} -- --nocapture
+
+ doc:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ permissions:
+ contents: write
+ env:
+ default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
+ RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
+ steps:
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@nightly
+ - name: Build docs
+ continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
+ run: |
+ cargo doc --no-deps --all-features
+ printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
+ - name: Deploy to Github Pages
+ if: ${{ github.ref == env.default-branch }}
+ uses: JamesIves/github-pages-deploy-action@v4
+ with:
+ single-commit: true
+ branch: gh-pages
+ folder: target/doc
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..23bb4fa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/target
+/.vscode
+.DS_Store
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..67569e7
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "ratio"
+version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..a914840
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "ratio"
+version = "0.1.0"
+edition = "2021"
+authors = ["Yuekai Jia "]
+description = "The type of ratios and related operations"
+license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
+homepage = "https://github.com/arceos-org/arceos"
+repository = "https://github.com/arceos-org/arceos/tree/main/crates/ratio"
+documentation = "https://arceos-org.github.io/arceos/ratio/index.html"
+
+[dependencies]
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..5ae5383
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,187 @@
+//! The type of ratios and related operations.
+//!
+//! A **ratio** is the result of dividing two integers, i.e., the numerator and
+//! denominator.
+//!
+//! # Examples
+//!
+//! ```
+//! use ratio::Ratio;
+//!
+//! let ratio = Ratio::new(1, 3); // 1 / 3
+//! assert_eq!(ratio.mul_trunc(20), 6); // trunc(20 * 1 / 3) = trunc(6.66..) = 6
+//! assert_eq!(ratio.mul_round(20), 7); // round(20 * 1 / 3) = round(6.66..) = 7
+//! println!("{:?}", ratio); // Ratio(1/3 ~= 1431655765/4294967296)
+//! ```
+
+#![cfg_attr(not(test), no_std)]
+
+use core::{cmp::PartialEq, fmt};
+
+/// The ratio type.
+///
+/// It converts `numerator / denominator` to `mult / (1 << shift)` to avoid
+/// `u128` division on calculation. The `shift` is as large as possible to
+/// improve precision.
+///
+/// Currently, it only supports `u32` as the numerator and denominator.
+pub struct Ratio {
+ numerator: u32,
+ denominator: u32,
+ mult: u32,
+ shift: u32,
+}
+
+impl Ratio {
+ /// The zero ratio.
+ pub const fn zero() -> Self {
+ Self {
+ numerator: 0,
+ denominator: 0,
+ mult: 0,
+ shift: 0,
+ }
+ }
+
+ /// Creates a new ratio `numerator / denominator`.
+ pub const fn new(numerator: u32, denominator: u32) -> Self {
+ assert!(!(denominator == 0 && numerator != 0));
+ if numerator == 0 {
+ return Self {
+ numerator,
+ denominator,
+ mult: 0,
+ shift: 0,
+ };
+ }
+
+ // numerator / denominator == (numerator * (1 << shift) / denominator) / (1 << shift)
+ let mut shift = 32;
+ let mut mult;
+ loop {
+ mult = (((numerator as u64) << shift) + denominator as u64 / 2) / denominator as u64;
+ if mult <= u32::MAX as u64 || shift == 0 {
+ break;
+ }
+ shift -= 1;
+ }
+
+ while mult % 2 == 0 && shift > 0 {
+ mult /= 2;
+ shift -= 1;
+ }
+
+ Self {
+ numerator,
+ denominator,
+ mult: mult as u32,
+ shift,
+ }
+ }
+
+ /// Get the inverse ratio.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ratio::Ratio;
+ ///
+ /// let ratio = Ratio::new(1, 2);
+ /// assert_eq!(ratio.inverse(), Ratio::new(2, 1));
+ /// ```
+ pub const fn inverse(&self) -> Self {
+ Self::new(self.denominator, self.numerator)
+ }
+
+ /// Multiplies the ratio by a value and rounds the result down.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ratio::Ratio;
+ ///
+ /// let ratio = Ratio::new(2, 3);
+ /// assert_eq!(ratio.mul_trunc(99), 66); // 99 * 2 / 3 = 66
+ /// assert_eq!(ratio.mul_trunc(100), 66); // trunc(100 * 2 / 3) = trunc(66.66...) = 66
+ /// ```
+ pub const fn mul_trunc(&self, value: u64) -> u64 {
+ ((value as u128 * self.mult as u128) >> self.shift) as u64
+ }
+
+ /// Multiplies the ratio by a value and rounds the result to the nearest
+ /// whole number.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use ratio::Ratio;
+ ///
+ /// let ratio = Ratio::new(2, 3);
+ /// assert_eq!(ratio.mul_round(99), 66); // 99 * 2 / 3 = 66
+ /// assert_eq!(ratio.mul_round(100), 67); // round(100 * 2 / 3) = round(66.66...) = 67
+ /// ```
+ pub const fn mul_round(&self, value: u64) -> u64 {
+ ((value as u128 * self.mult as u128 + (1 << self.shift >> 1)) >> self.shift) as u64
+ }
+}
+
+impl fmt::Debug for Ratio {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "Ratio({}/{} ~= {}/{})",
+ self.numerator,
+ self.denominator,
+ self.mult,
+ 1u64 << self.shift
+ )
+ }
+}
+
+impl PartialEq for Ratio {
+ #[inline]
+ fn eq(&self, other: &Ratio) -> bool {
+ self.mult == other.mult && self.shift == other.shift
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_ratio() {
+ let a = Ratio::new(625_000, 1_000_000);
+ let b = Ratio::new(1, u32::MAX);
+ let c = Ratio::new(u32::MAX, u32::MAX);
+ let d = Ratio::new(u32::MAX, 1);
+
+ assert_eq!(a.mult, 5);
+ assert_eq!(a.shift, 3);
+ assert_eq!(a.mul_trunc(800), 500);
+
+ assert_eq!(b.mult, 1);
+ assert_eq!(b.shift, 32);
+ assert_eq!(b.mul_trunc(u32::MAX as _), 0);
+ assert_eq!(b.mul_round(u32::MAX as _), 1);
+
+ assert_eq!(c.mult, 1);
+ assert_eq!(c.shift, 0);
+ assert_eq!(c.mul_trunc(u32::MAX as _), u32::MAX as _);
+
+ println!("{:?}", a);
+ println!("{:?}", b);
+ println!("{:?}", c);
+ println!("{:?}", d);
+ }
+
+ #[test]
+ fn test_zero() {
+ let z1 = Ratio::new(0, 100);
+ let z2 = Ratio::zero();
+ let z3 = Ratio::new(0, 0);
+ assert_eq!(z1.mul_trunc(233), 0);
+ assert_eq!(z2.mul_trunc(0), 0);
+ assert_eq!(z3.mul_round(456), 0);
+ }
+}