Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two's complement #41

Closed
wants to merge 13 commits into from
7 changes: 7 additions & 0 deletions sway_libs/src/lib.sw
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
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 string/string;
47 changes: 47 additions & 0 deletions sway_libs/src/signed_integers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Overview

The Signed Integers library provides an interface 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();
```

For more information please see the [specification](./SPECIFICATION.md).
35 changes: 35 additions & 0 deletions sway_libs/src/signed_integers/SPECIFICATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 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.

### `from_uint()`

Helper function to get a positive value from unsigned number

### `max()`

The largest value that can be represented by this type.

### `min()`

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

### `neg_from`

Helper function to get a negative value of unsigned number

### Basic arithmetic operations

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

pub trait TwosComplement {
fn twos_complement(self) -> Self;
}


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

use std::u128::U128;
use ::signed_integers::common::Error;
use ::signed_integers::common::TwosComplement;

/// 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,
}

pub trait From {
/// Function for creating I128 from U128
fn from(underlying: U128) -> Self;
}

impl From for I128 {
/// Helper function to get a signed number from with an underlying
fn from(underlying: U128) -> Self {
Self { underlying }
}
}

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 underlying value that corresponds to zero signed value
pub fn indent() -> U128 {
U128 {
upper: 1,
lower: 0,
}
}
}

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

/// Helper function to get a positive value from unsigned number
fn from_uint(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 }
}

/// The largest value that can be represented by this 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 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
}
}

impl TwosComplement for I128 {
fn twos_complement(self) -> Self {
let u128_one = U128 {
upper: 0,
lower: 1,
};
let one = ~I128::from_uint(u128_one);
let res = !self - one;
res
}
}
Loading