Skip to content

Commit

Permalink
Signed integers (#30)
Browse files Browse the repository at this point in the history
## Type of change

<!--Delete points that do not apply-->

- New feature

## Changes

The following changes have been made:

Addition of signed typers: `i8`, `i16`, `i32`, `i64`

## Notes

- Note 1

## Related Issues

<!--Delete everything after the "#" symbol and replace it with a number.
No spaces between hash and number-->

Closes #36

Co-authored-by: tyshkor <[email protected]>
Co-authored-by: Cameron Carstens <[email protected]>
Co-authored-by: Simon <[email protected]>
  • Loading branch information
4 people authored Nov 14, 2022
1 parent 98ed79c commit 2c99891
Show file tree
Hide file tree
Showing 43 changed files with 1,478 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ These libraries contain helper functions, generalized standards, and other tools
- [Binary Merkle Proof](./sway_libs/src/merkle_proof/) is used to verify Binary Merkle Trees computed off-chain.
- [Non-Fungible Token (NFT)](./sway_libs/src/nft/) is a token library which provides unqiue collectibles, identified and differentiated by token IDs.
- [String](./sway_libs/src/string/) is an interface to implement dynamic length strings that are UTF-8 encoded.
- [Signed Integers](./sway_libs/src/signed_integers/) is an interface to implement signed integers.

## Using a library

Expand Down
7 changes: 7 additions & 0 deletions sway_libs/src/lib.sw
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
library sway_libs;

dep merkle_proof/binary_merkle_proof;
dep signed_integers/signed_integers;
dep signed_integers/i8;
dep signed_integers/i16;
dep signed_integers/i32;
dep signed_integers/i64;
dep signed_integers/i128;
dep signed_integers/i256;
dep nft/nft;
dep string/string;
48 changes: 48 additions & 0 deletions sway_libs/src/signed_integers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Overview

The Signed Integers library provides a library to use signed numbers in Sway. It has 6 distinct types: `I8`, `I16`, `I32`, `I64`, `I128`, `I256`. These types are stack allocated.

These types are stored as unsigned integers, therefore either `u64` or a number of them. Therefore the size can be known at compile time and the length is static.

For more information please see the [specification](./SPECIFICATION.md).

# Using the Library

## Getting Started

In order to use the `Signed Integers` type it must be added to the Forc.toml file and then imported into your Sway project. To add Sway-libs as a dependency to the Forc.toml in your project, please see the [README.md](../../../README.md).

```rust
use sway_libs::i8::I8;
```

Once imported, a `Signed Integer` type can be instantiated defining a new variable and calling the `new` function.

```rust
let mut i8_value = I8::new();
```

## Basic Functionality

Basic arithmetic operations are working as usual

```rust
// Add 2 signed values
let i8_value_3 = i8_value_1 + i8_value_2;

// Subtract one signed value from another
let i8_value_3 = i8_value_1 - i8_value_2;
```

Helper functions

```rust
// To get a negative value from an unsigned value
let neg_value = I8::neg_from();

// Maximum value
let max_i8_value = I8::max();
```

## Known Issues
The current implementation of `U128` and `U256` will compile large bytecode sizes when performing mathematical computations. As a result, `I128` and `I256` inherit the same issue and could cause high transaction costs. This should be resolved with future optimizations of the Sway compiler.
31 changes: 31 additions & 0 deletions sway_libs/src/signed_integers/SPECIFICATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Overview

This document provides an overview of the Signed Integers library.

It outlines the use cases, i.e. specification, and describes how to implement the library.

## Use Cases

The Signed Integers library can be used anytime a one needs negative numbers.

## Public Functions

### `bits()`

The size of this type in bits.

### `max()`

The largest value that can be represented by this integer type.

### `min()`

The smallest value that can be represented by this integer type.

### `neg_from`

Helper function to get a negative value of an unsigned number

### Basic arithmetic operations

`+`, `-`, `*`, `/`
5 changes: 5 additions & 0 deletions sway_libs/src/signed_integers/errors.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
library errors;

pub enum Error {
ZeroDivisor: (),
}
171 changes: 171 additions & 0 deletions sway_libs/src/signed_integers/i128.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
library i128;

use std::u128::U128;
use ::signed_integers::errors::Error;

/// The 128-bit signed integer type.
/// Represented as an underlying U128 value.
/// Actual value is underlying value minus 2 ^ 127
/// Max value is 2 ^ 127 - 1, min value is - 2 ^ 127
pub struct I128 {
underlying: U128,
}

impl I128 {
/// The underlying value that corresponds to zero signed value
pub fn indent() -> U128 {
U128 {
upper: 1,
lower: 0,
}
}
}

impl From<U128> for I128 {
/// Helper function to get a signed number from with an underlying
fn from(value: U128) -> Self {
// as the minimal value of I128 is -I128::indent() (1 << 63) we should add I128::indent() (1 << 63)
let underlying: U128 = value + Self::indent();
Self { underlying }
}

fn into(self) -> U128 {
self.underlying - Self::indent()
}
}

impl core::ops::Eq for I128 {
fn eq(self, other: Self) -> bool {
self.underlying == other.underlying
}
}

impl core::ops::Ord for I128 {
fn gt(self, other: Self) -> bool {
self.underlying > other.underlying
}

fn lt(self, other: Self) -> bool {
self.underlying < other.underlying
}
}

impl I128 {
/// The size of this type in bits.
pub fn bits() -> u32 {
128
}

/// Helper function to get a positive value from an unsigned number
fn from_uint(underlying: U128) -> Self {
Self { underlying }
}

/// The largest value that can be represented by this integer type,
pub fn max() -> Self {
Self {
underlying: U128::max(),
}
}

/// The smallest value that can be represented by this integer type.
pub fn min() -> Self {
Self {
underlying: U128::min(),
}
}

/// Helper function to get a negative value of an unsigned number
pub fn neg_from(value: U128) -> Self {
Self {
underlying: Self::indent() - value,
}
}

/// Initializes a new, zeroed I128.
pub fn new() -> Self {
Self {
underlying: Self::indent(),
}
}
}

impl core::ops::Add for I128 {
/// Add a I128 to a I128. Panics on overflow.
fn add(self, other: Self) -> Self {
// subtract 1 << 63 to avoid double move
Self::from(self.underlying - Self::indent() + other.underlying)
}
}

impl core::ops::Divide for I128 {
/// Divide a I128 by a I128. Panics if divisor is zero.
fn divide(self, divisor: Self) -> Self {
require(divisor != Self::new(), Error::ZeroDivisor);
let mut res = Self::new();
if (self.underlying > Self::indent()
|| self.underlying == Self::indent())
&& divisor.underlying > Self::indent()
{
res = Self::from((self.underlying - Self::indent()) / (divisor.underlying - Self::indent()) + Self::indent());
} else if self.underlying < Self::indent()
&& divisor.underlying < Self::indent()
{
res = Self::from((Self::indent() - self.underlying) / (Self::indent() - divisor.underlying) + Self::indent());
} else if (self.underlying > Self::indent()
|| self.underlying == Self::indent())
&& divisor.underlying < Self::indent()
{
res = Self::from(Self::indent() - (self.underlying - Self::indent()) / (Self::indent() - divisor.underlying));
} else if self.underlying < Self::indent()
&& divisor.underlying > Self::indent()
{
res = Self::from(Self::indent() - (Self::indent() - self.underlying) / (divisor.underlying - Self::indent()));
}
res
}
}

impl core::ops::Multiply for I128 {
/// Multiply a I128 with a I128. Panics of overflow.
fn multiply(self, other: Self) -> Self {
let mut res = Self::new();
if (self.underlying > Self::indent()
|| self.underlying == Self::indent())
&& (other.underlying > Self::indent()
|| other.underlying == Self::indent())
{
res = Self::from((self.underlying - Self::indent()) * (other.underlying - Self::indent()) + Self::indent());
} else if self.underlying < Self::indent()
&& other.underlying < Self::indent()
{
res = Self::from((Self::indent() - self.underlying) * (Self::indent() - other.underlying) + Self::indent());
} else if (self.underlying > Self::indent()
|| self.underlying == Self::indent())
&& other.underlying < Self::indent()
{
res = Self::from(Self::indent() - (self.underlying - Self::indent()) * (Self::indent() - other.underlying));
} else if self.underlying < Self::indent()
&& (other.underlying > Self::indent()
|| other.underlying == Self::indent())
{
res = Self::from(Self::indent() - (other.underlying - Self::indent()) * (Self::indent() - self.underlying));
}
res
}
}

impl core::ops::Subtract for I128 {
/// Subtract a I128 from a I128. Panics of overflow.
fn subtract(self, other: Self) -> Self {
let mut res = Self::new();
if self > other {
// add 1 << 63 to avoid loosing the move
res = Self::from(self.underlying - other.underlying + Self::indent());
} else {
// subtract from 1 << 63 as we are getting a negative value
res = Self::from(Self::indent() - (other.underlying - self.underlying));
}
res
}
}
Loading

0 comments on commit 2c99891

Please sign in to comment.