How to address an array index by a negative offset? - rust

Say I have something like this:
let SIZE = 100;
let a = [0; SIZE];
let b = -1;
How can I then do something like:
a[(SIZE/2)+b];
There's a mismatch here between the usize index of the array and the i32 of the negative number being subtracted. Is there a convenient way of indexing through an array also with negative offsets?

Like #Aplet123 answered, you may use casts. But if b is always negative, you could save the absolute value and just substact it instead:
let b = 1;
return a[SIZE/2 - b];

Cast it to an isize (a signed integer with the same size as a usize) first:
a[((SIZE / 2) as isize + b) as usize]

Related

Pass in int to function and return float Rust

I am very new to Rust and am trying to dip my toes in it a bit and ran into issues trying to work with integers and floats. I am trying to figure out how to take in an integer as a variable for a recursive logarithm function that will return a float. The issue is that my return value is still an integer.
fn log(x: f64,b: f64) -> f64 {
if x < b {
return 0.0;
}
let result = 1.0 + log(x / b, b);
return result;
}
fn main() {
let test_num1: i64 = 3;
let test_num2: i64 = 11;
let y: f64 = log(test_num2 as f64, test_num1 as f64);
println!("\nDEBUGGING RESULT = {}\n", y as f64);
}
However my code outputs this:
DEBUGGING RESULT = 2
but am expecting something along the lines of 2.182...
What is the proper way to allow for integers to be manipulated and assigned as floats? Any help is much appreciated!
Update:
The error was in my code :) in the log() function specifically. Sebastian pointed out where the logic is a bit messed up!
but am expecting something along the lines of 2.182...
Your expectation is just wrong. Consider what your log function might do: either it returns 0, or it returns 1 plus whatever the recursive call returns. Therefore, it can only ever return whole numbers (and specifically, it returns how often you can divide x by b before x becomes smaller, which is 2 in your test case).

Datatype for indexing vector

I just started to learn Rust. I understand that Rust's for loop indices and vector indices must be of type usize, hence I have written the following code. The computation of j within the for loop requires i to be type u32, so I convert it. Now, I have to change the type of i and j again to get the vector items.
I would like to avoid this constant back-and-forth conversion, is there an alternate way to do this in Rust? Thank you for your help.
fn compute(dots: Vec, N: u32) -> f32 {
let mut j: u32;
let mut value: f32 = 0.0;
for i in 0..N {
j = (i as u32 + 1) % N;
value += dots[i as usize].a * dots[j as usize].b;
value -= dots[i as usize].b * dots[j as usize].a;
}
return value
}
Either change the function signature to use N: usize, or, if you can't do that, just let M = N as usize and loop over 0..M (the loop variable will then have type usize).
Be aware that in real code, you need to be sure that usize is at least as wide as u32 if you opt for the conversion. If you cannot assure that, use try_into instead of as to convert.

Getting the length of an int

I am trying to get the length (the number of digits when interpreted in decimal) of an int in rust. I found a way to do it, however am looking for method that comes from the primitive itself. This is what I have:
let num = 90.to_string();
println!("num: {}", num.chars().count())
// num: 2
I am looking at https://docs.rs/digits/0.3.3/digits/struct.Digits.html#method.length. is this a good candidate? How do I use it? Or are there other crates that does it for me?
One liners with less type conversion is the ideal solution I am looking for.
You could loop and check how often you can divide the number by 10 before it becomes a single digit.
Or in the other direction (because division is slower than multiplication), check how often you can multiply 10*10*...*10 until you reach the number:
fn length(n: u32, base: u32) -> u32 {
let mut power = base;
let mut count = 1;
while n >= power {
count += 1;
if let Some(new_power) = power.checked_mul(base) {
power = new_power;
} else {
break;
}
}
count
}
With nightly rust (or in the future, when the int_log feature is stabilized), you can use:
#![feature(int_log)]
n.checked_log10().unwrap_or(0) + 1
Here is a one-liner that doesn't require strings or floating point:
println!("num: {}", successors(Some(n), |&n| (n >= 10).then(|| n / 10)).count());
It simply counts the number of times the initial number needs to be divided by 10 in order to reach 0.
EDIT: the first version of this answer used iterate from the (excellent and highly recommended) itertools crate, but #trentcl pointed out that successors from the stdlib does the same. For reference, here is the version using iterate:
println!("num: {}", iterate(n, |&n| n / 10).take_while(|&n| n > 0).count().max(1));
Here's a (barely) one-liner that's faster than doing a string conversion, using std::iter stuff:
let some_int = 9834;
let decimal_places = (0..).take_while(|i| 10u64.pow(*i) <= some_int).count();
The first method below relies on the following formula, where a and b are the logarithmic bases.
log<a>( x ) = log<b>( x ) / log<b>( a )
log<a>( x ) = log<2>( x ) / log<2>( a ) // Substituting 2 for `b`.
The following function can be applied to finding the number of digits for bases that are a power of 2. This approach is very fast.
fn num_digits_base_pow2(n: u64, b: u32) -> u32
{
(63 - n.leading_zeros()) / (31 - b.leading_zeros()) + 1
}
The bits are counted for both n (the number we want to represent) and b (the base) to find their log2 floor values. Then the adjusted ratio of these values gives the ceiling log value in the desired base.
For a general purpose approach to finding the number of digits for arbitrary bases, the following should suffice.
fn num_digits(n: u64, b: u32) -> u32
{
(n as f64).log(b as f64).ceil() as u32
}
if num is signed:
let digits = (num.abs() as f64 + 0.1).log10().ceil() as u32;
A nice property of numbers that is always good to have in mind is that the number of digits required to write a number $x$ in base $n$ is actually $\lceil log_n(x + 1) \rceil$.
Therefore, one can simply write the following function (notice the cast from u32 to f32, since integers don't have a log function).
fn length(n: u32, base: u32) -> u32 {
let n = (n+1) as f32;
n.log(base as f32).ceil() as u32
}
You can easily adapt it for negative numbers. For floating point numbers this might be a bit (i.e. a lot) more tricky.
To take into account Daniel's comment about the pathological cases introduced by using f32, note that, with nightly Rust, integers have a logarithm method. (Notice that, imo, those are implementation details, and you should more focus on understanding the algorithm than the implementation.):
#![feature(int_log)]
fn length(n: u32, base: u32) -> u32 {
n.log(base) + 1
}

Convert array (or vector) of u16 (or u32, u64) to array of u8

I have no problem to do it for u16 to u8 using bit shifts and cast but how could I do it with an array of u16? Even ideally I would prefer to convert directly from vec to [u8]. What would be the most elegant way to do it?
&my_vector[..] // my vector converted to [u16] but I need [u8]
Was able to make it work thanks to #Aplet123 insight:
From Vector to bytes array
From Vec to [u8]
let mut my_u16_vec : Vec<u16> = Vec::new();
let my_u8_array = my_u16_vec.align_to::<u8>().1;
From bytes array back to Vector
From [u8] to Vec
let n = my_u16_vec.len() * 2;
let my_u16_vec_bis:Vec<u16> = (my_u8_array[..n].align_to::<u16>().1).to_vec();
Getting the bytes right
And then reverse bytes as values are written reversely in memory due to endianness:
for e in my_u16_vec_bis:Vec.iter() {
let value = e >> 8 | (e & 0xff) << 8;
}

Bitwise operations, comparing a u32 with a byte array

Lets say I have the value 1025 as a byte array and the value 1030 as usize. How would I go about comparing if the byte array is bigger, lesser or equal without deserializing it?
I'm completely stuck, I assume the easisest way is to get the biggest bytes of the byte array, its position, then bitshift the u32 and see if any bits in the byte is set, if not the byte array is bigger.
In short I want to write some functions to be able to decide if a > b, a < b and a == b.
To use a code example
fn is_greater(a: &[u8], b: usize) -> bool {
// a is LE, so reverse and get the largest bytes
let c = a.iter()
.enumerate()
.rev()
.filter_map(|(i, b)| ( if *b != 0 { return Some((i, *b)); } else { None }))
.collect::<Vec<(usize, u8)>>();
for (i, be) in c {
let k = (b >> (i * 8)) & 255;
println!("{}, {}", be, k);
return be as usize > k
}
false
}
EDIT: Should have clarified, the byte array can be any integer, unsigned integer or float. Simply any integer bincode::serialize can serialize.
I also had in mind to avoid converting the byte array, comparison is supposed to be used on 100000 of byte arrays, so I assume bit operations is the preferred way.
No need for all those extra steps. The basic problem is to know if the integer encoded in the byte-array is little endian, big endian or native endian. Knowing that, you can use usize::from_??_bytes to convert a fixed-size array to an integer; use the TryFrom-trait to get the fixed-size array from the slice.
fn is_greater(b: &[u8], v: usize) -> Result<bool, std::array::TryFromSliceError> {
use std::convert::TryFrom;
Ok(usize::from_le_bytes(<[u8; 8]>::try_from(b)?) > v)
}
This function will return an error if the byte-slice is smaller than 8 bytes, in which case there is no way to construct a usize; you can also convert to u32 or even u16, upcast that to usize and then do the comparison. Also notice that this example uses from_le_bytes, assuming the bytes-slice contains an integer encoded as little endian.

Resources