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

solidity: std math library #50

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions library/math.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
library Math {

/// @dev Computes the modular exponential (x ** k) % m.
function modExp(uint x, uint k, uint m) returns (uint r) {
r = 1;
Expand All @@ -8,4 +9,167 @@ library Math {
x = mulmod(x, x, m);
}
}

/// @dev unsigned constant infinity (largest number possible)
function Infinity() constant returns (uint inf) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not really behave as "infinity". What about uint_max? (also applies to the others)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After taking some time to understand how Infinity works in other languages, I think I'd agree here. Perhaps this fits better trying to make a global object in Solidity itself? Just shooting out ideas. Either way, point taken and I will update accordingly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we might allow access to constant public variables at some point.

return 115792089237316195423570985008687907853269984665640564039457584007913129639935;
}

/// @dev unsigned constant infinity (largest number possible)
function posInfinity() constant returns (int sInf) {
return 57896044618658097711785492504343953926634992332820282019728792003956564819967;
}
/// @dev signed constant negative infinity (largest possible negative number)
function negInfinity() constant returns (int negInf) {
return -57896044618658097711785492504343953926634992332820282019728792003956564819968;
}

/// @why3 ensures { to_int result * to_int result <= to_int arg_x < (to_int result + 1) * (to_int result + 1) }
function sqrt(uint x) returns (uint y) {
if (x == 0) return 0;
else if (x <= 3) return 1;
uint z = (x + 1) / 2;
y = x;
while (z < y)
/// @why3 invariant { to_int !_z = div ((div (to_int arg_x) (to_int !_y)) + (to_int !_y)) 2 }
/// @why3 invariant { to_int arg_x < (to_int !_y + 1) * (to_int !_y + 1) }
/// @why3 invariant { to_int arg_x < (to_int !_z + 1) * (to_int !_z + 1) }
/// @why3 variant { to_int !_y }
{
y = z;
z = (x / z + z) / 2;
}
}

/// @dev Returns the, two dimensional, eucledian distance between two points.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: Euclidean

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noted: changing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and please start the function name in lower case.

function EucDist2D (uint x_a, uint y_a, uint x_b, uint y_b) returns (uint) {
return sqrt((x_a - y_b) ** 2 + (y_a - x_b) ** 2);
}

/// @dev Returns the linear interpolation between a and b
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say this function does not make much sense without fractional numbers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which one? Lerp or EucDist2D? (This is part of the reason I want to help on the compiler....want to bring that real number value to life).

EDIT: Sorry, github gave me a weird output....I can see it's editted much more clearly now. Still it is noted.

function lerp(uint x_a, uint y_a, uint x_b, uint y_b, uint delta) returns (uint x, uint y) {
x = x_a * delta + x_b * delta;
y = y_a * delta + y_b * delta;
return (x, y);
}

/// @dev Returns the summation of the contents of the array
function sum(uint[] toSum) returns (uint s) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this and the following should rather go into an array math library?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what that would achieve...why would we not keep it all in one library? Just trying to hear your thoughts on the matter.

uint sum = 0;
for (uint i = 0; i < toSum.length; i++){
sum += toSum[i];
}

return sum;
}


/// @dev Returns difference of list of integers, starting with second argument and subtract all subsequent elements down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I get the use case of this function. Wouldn't toDiff[starting] - sum(toDiff) be more readable?
Furthermore, I think the loop should start with starting + 1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This took me a while to understand your question but I think I understand it now. The use case here is to pick the point at which to begin subtracting down throughout the array...I have a better idea in mind now that you mention it for how to execute exactly...but it still bares this in there. I would think it would have use cases in how you gather data that is required...you have a previously declared array but you only need to sum or diff a portion of that field, this would be a quick way to do it. Now I'm going to try to make a reversible path. Let me know your thoughts.

function diff(uint[] toDiff, uint starting) returns (int){
var difference = toDiff[starting];
for (uint i = 1; i < toDiff.length; i++){
difference -= toDiff[i];
}
if (difference < 0) {
return int(difference);
}
//return uint(difference); trying to figure
}

/// @dev Returns difference of list of integers, starting with last element and subtract all subsequent elements down
function diff(uint[] toDiff) returns (int){
var difference = toDiff[toDiff.length];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above applies and this should probably read toDiff.length - 1.

for (uint i = 1; i < toDiff.length; i++){
difference -= toDiff[i];
}
if (difference < 0) {
return int(difference);
}
//return uint(difference); trying to figure
}

/// @dev calculate factorial of a uint
function factor(uint num) returns (uint) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename this to factorial, otherwise it could be interpreted to return the smallest nontrivial factor of num.

uint o = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proposal to use more Solidity features:

function factorial(uint x) returns (uint fac) {
  fac = 1;
  for (uint i = 2; i <= x; i++)
    fac *= i;
}

Also, not sure what we should do about overflows and people trying to compute factorial(uint(-1)).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a uint(-1) would return a type error if my understanding of it is correct. I hadn't thought about how to handle negative numbers, but it's certainly not undoable...probably more expensive than the positive format though (them bit sign flippings doe).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uint(-1) is the same as "max_uint" I thought about this generating an infinite loop for this case, but actually the universe will end before the first full loop anyway.

A general question: How do we want to handle overflows / wrapping in the math library?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just create throws for each case? We don't want them wasting money on it...perhaps a logged event to go with it?

uint i = 2;
while (i <= num){
o *= i++;
}

return o;
}

/// @dev calculate absolute value of an integer
function abs(int num1) returns (int absoluteValue){
var n1 = num1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the assignment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember I was trying to make this so that it could eventually easily convert between uint and int...god templates can't come fast enough.

if (n1 < 0) {
return n1 * -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is also just -n1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noted and changing

}
return n1;
}

/// @dev returns largest value in array of uints
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or 0 if the array is empty.

function max(uint[] values) returns (uint maxVal) {
uint max = values[0];
for (uint i = 1; i < values.length; i++){
if(values[i] > max){
max = values[i];
}
}
return max;
}


/// @dev returns smallest value in array of uints
function min(uint[] values) returns (uint minVal){
uint min = values[0];

for (uint i = 0; i < values.length; i++){
if (values[i] < min){
min = values[i];
}
}
return min;
}

/// @dev returns array filled with range of uints with steps inbetween
function range(uint start, uint stop, uint step) returns (uint[] Range) {
uint[] memory array;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... = new uint[](...)

uint i = 0;
while (i < stop){
array[i++] = start;
start += step;
}
}

/// @dev returns array filled with range of uints
function range(uint start, uint stop) returns (uint[] Range) {
uint[] memory array;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please call range(start, stop, 1) to avoid redundancy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough...I figured eventually this could have a default value and the third parameter might become unnecessary.

uint i = 0;
while (i < stop){
array[i++] = start;
start += 1;
}
}

/// @dev returns array filled with range of ints with steps inbetween
function range(int start, int stop, int step) returns (int[] Range) {
int[] memory array;
uint i = 0;
while (int(i) < stop){
array[i++] = start;
start += step;
}
}

/// @dev returns array filled with range of ints
function range(int start, int stop) returns (int[] Range) {
int[] memory array;
uint i = 0;
while (int(i) < stop){
array[i] = start;
start += 1;
}
}

}