Skip to content

Commit

Permalink
Turn largest-series-product lib.cairo back into scaffold
Browse files Browse the repository at this point in the history
  • Loading branch information
Nenad committed Jun 27, 2024
1 parent 6f5a0ed commit e47db75
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 129 deletions.
57 changes: 29 additions & 28 deletions exercises/practice/largest-series-product/.meta/example.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub enum Error {
#[derive(Drop, Copy)]
struct Product {
value: u64,
from: u32,
start_index: u32,
}

pub fn lsp(input: @ByteArray, span: i32) -> Result<u64, Error> {
Expand All @@ -30,64 +30,65 @@ pub fn lsp(input: @ByteArray, span: i32) -> Result<u64, Error> {

// calculate first max product
// use '?' to propagate the error if it occurred
let product = product_from(input, span, 0, span, Product { value: 1, from: 0 })?;
let product = product_from(input, span, 0, span, Product { value: 1, start_index: 0 })?;

next_max_product(input, span, product.value, product)
max_product(input, span, product.value, product.start_index, product.value)
}

fn product_from(
input: @ByteArray, span: u32, from: u32, remaining: u32, product: Product
input: @ByteArray, span: u32, from: u32, remaining: u32, accumulated: Product
) -> Result<Product, Error> {
if remaining == 0 {
return Result::Ok(product);
return Result::Ok(accumulated);
}
if from + remaining > input.len() {
return Result::Ok(Product { value: 0, from });
// we couldn't find a span of non-zero digits,
// so all products must be zero
return Result::Ok(Product { value: 0, start_index: from });
}

let digit = input.at(from).try_into_digit()?;
if digit != 0 {

if digit == 0 {
return product_from(
input, span, from + 1, span, Product { value: 1, start_index: from + 1 }
);
} else {
return product_from(
input,
span,
from + 1,
remaining - 1,
Product { value: product.value * digit, from: product.from }
Product { value: accumulated.value * digit, start_index: accumulated.start_index }
);
} else {
return product_from(input, span, from + 1, span, Product { value: 1, from: from + 1 });
}
}

fn next_max_product(
input: @ByteArray, span: u32, max: u64, current_product: Product
fn max_product(
input: @ByteArray, span: u32, previous: u64, from: u32, max: u64
) -> Result<u64, Error> {
if current_product.from + span >= input.len() {
if from + span >= input.len() {
return Result::Ok(max);
}

// safe to unwrap, we already processed this digit before
let reduced_value = current_product.value
/ input.at(current_product.from).try_into_digit().unwrap();
let next_digit = input.at(from + span).try_into_digit()?;

let next_digit = input.at(current_product.from + span).try_into_digit()?;

let product = if next_digit == 0 {
let next = if next_digit == 0 {
// every product that includes digit 0 will be 0,
// so calculate the next product after the digit 0
product_from(
input,
span,
current_product.from + span + 1,
span,
Product { value: 1, from: current_product.from + span + 1 }
input, span, from + span + 1, span, Product { value: 1, start_index: from + span + 1 }
)?
} else {
Product { value: reduced_value * next_digit, from: current_product.from + 1 }
// safe to unwrap, we already processed this digit before
let common_product = previous / input.at(from).try_into_digit().unwrap();
Product { value: common_product * next_digit, start_index: from + 1 }
};

if product.value > max {
next_max_product(input, span, product.value, product)
if next.value > max {
max_product(input, span, next.value, next.start_index, next.value)
} else {
next_max_product(input, span, max, product)
max_product(input, span, next.value, next.start_index, max)
}
}

Expand Down
102 changes: 1 addition & 101 deletions exercises/practice/largest-series-product/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,110 +3,10 @@ pub enum Error {
SpanTooLong,
InvalidDigit: u8,
NegativeSpan,
IndexOutOfBounds
}

#[derive(Drop, Copy)]
struct Product {
value: u64,
start_index: u32,
}

pub fn lsp(input: @ByteArray, span: i32) -> Result<u64, Error> {
// validate span
if span == 0 {
return Result::Ok(1);
}
if span < 0 {
return Result::Err(Error::NegativeSpan);
}

// shadowing to make span of unsigned type
let span: u32 = span.try_into().unwrap();

if span > input.len() {
return Result::Err(Error::SpanTooLong);
}

// calculate first max product
// use '?' to propagate the error if it occurred
let product = product_from(input, span, 0, span, Product { value: 1, start_index: 0 })?;

max_product(input, span, product.value, product.start_index, product.value)
}

fn product_from(
input: @ByteArray, span: u32, from: u32, remaining: u32, accumulated: Product
) -> Result<Product, Error> {
if remaining == 0 {
return Result::Ok(accumulated);
}
if from + remaining > input.len() {
// we couldn't find a span of non-zero digits,
// so all products must be zero
return Result::Ok(Product { value: 0, start_index: from });
}

let digit = input.at(from).try_into_digit()?;

if digit == 0 {
return product_from(
input, span, from + 1, span, Product { value: 1, start_index: from + 1 }
);
} else {
return product_from(
input,
span,
from + 1,
remaining - 1,
Product { value: accumulated.value * digit, start_index: accumulated.start_index }
);
}
}

fn max_product(
input: @ByteArray, span: u32, previous: u64, from: u32, max: u64
) -> Result<u64, Error> {
if from + span >= input.len() {
return Result::Ok(max);
}

let next_digit = input.at(from + span).try_into_digit()?;

let next = if next_digit == 0 {
// every product that includes digit 0 will be 0,
// so calculate the next product after the digit 0
product_from(
input, span, from + span + 1, span, Product { value: 1, start_index: from + span + 1 }
)?
} else {
// safe to unwrap, we already processed this digit before
let common_product = previous / input.at(from).try_into_digit().unwrap();
Product { value: common_product * next_digit, start_index: from + 1 }
};

if next.value > max {
max_product(input, span, next.value, next.start_index, next.value)
} else {
max_product(input, span, next.value, next.start_index, max)
}
}

/// Helper functions
#[generate_trait]
impl ByteArrayCharToU64 of ByteArrayCharToU64Trait {
fn try_into_digit(self: @Option<u8>) -> Result<u64, Error> {
if let Option::Some(char) = (*self) {
// ASCII digits are characters 48 through 57
if 48 <= char && char <= 57 {
Result::Ok(char.into() - 48)
} else {
Result::Err(Error::InvalidDigit(char))
}
} else {
Result::Err(Error::IndexOutOfBounds)
}
}
panic!("implement the 'lsp' function")
}

#[cfg(test)]
Expand Down

0 comments on commit e47db75

Please sign in to comment.