From fa3f7e75c3f4074cd0f47cb80c510b5a0916de20 Mon Sep 17 00:00:00 2001 From: Erim Date: Mon, 18 Sep 2023 11:32:56 +0300 Subject: [PATCH] feat: dedup for arrays (#174) ## Pull Request type Please check the type of change your PR introduces: - [ ] Bugfix - [x] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build-related changes - [ ] Documentation content changes - [ ] Other (please describe): ## What is the current behavior? Issue Number: N/A ## What is the new behavior? - - - ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information --------- Co-authored-by: gaetbout Co-authored-by: Lucas @ StarkWare <70894690+LucasLvy@users.noreply.github.com> --- src/data_structures/src/array_ext.cairo | 31 ++++++ .../src/tests/array_ext_test.cairo | 105 ++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/src/data_structures/src/array_ext.cairo b/src/data_structures/src/array_ext.cairo index e6ab14d7..a1420a3c 100644 --- a/src/data_structures/src/array_ext.cairo +++ b/src/data_structures/src/array_ext.cairo @@ -21,6 +21,7 @@ trait ArrayTraitExt { fn index_of_max, impl TPartialOrd: PartialOrd>( self: @Array ) -> Option; + fn dedup>(self: @Array) -> Array; } trait SpanTraitExt { @@ -43,6 +44,7 @@ trait SpanTraitExt { fn index_of_max, impl TPartialOrd: PartialOrd>( self: Span ) -> Option; + fn dedup>(self: Span) -> Array; } impl ArrayImpl, impl TDrop: Drop> of ArrayTraitExt { @@ -135,6 +137,10 @@ impl ArrayImpl, impl TDrop: Drop> of ArrayTraitExt ) -> Option { self.span().index_of_max() } + + fn dedup>(mut self: @Array) -> Array { + self.span().dedup() + } } impl SpanImpl, impl TDrop: Drop> of SpanTraitExt { @@ -354,4 +360,29 @@ impl SpanImpl, impl TDrop: Drop> of SpanTraitExt { index += 1; } } + + fn dedup>(mut self: Span) -> Array { + if self.len() == 0 { + return array![]; + } + + let mut last_value = self.pop_front().unwrap(); + let mut ret = array![*last_value]; + + loop { + match self.pop_front() { + Option::Some(v) => { + if (last_value != v) { + last_value = v; + ret.append(*v); + }; + }, + Option::None => { + break; + } + }; + }; + + ret + } } diff --git a/src/data_structures/src/tests/array_ext_test.cairo b/src/data_structures/src/tests/array_ext_test.cairo index 9ed7fd0f..e4f95426 100644 --- a/src/data_structures/src/tests/array_ext_test.cairo +++ b/src/data_structures/src/tests/array_ext_test.cairo @@ -3,6 +3,111 @@ use array::{ArrayTrait, SpanTrait}; use alexandria_data_structures::array_ext::{ArrayTraitExt, SpanTraitExt}; +// dedup + +#[test] +#[available_gas(2000000)] +fn dedup_all_different() { + let mut destination = array![1, 2, 3, 4]; + let new_arr = destination.dedup(); + let len = new_arr.len(); + + assert(*new_arr[0] == 1, 'Should be 1'); + assert(*new_arr[1] == 2, 'Should be 2'); + assert(*new_arr[2] == 3, 'Should be 3'); + assert(*new_arr[3] == 4, 'Should be 4'); + assert(new_arr.len() == 4, 'Len should be 4'); +} + +#[test] +#[available_gas(2000000)] +fn dedup_one_match() { + let mut destination = array![1, 2, 2, 3, 4]; + let new_arr = destination.dedup(); + let len = new_arr.len(); + + assert(*new_arr[0] == 1, 'Should be 1'); + assert(*new_arr[1] == 2, 'Should be 2'); + assert(*new_arr[2] == 3, 'Should be 3'); + assert(*new_arr[3] == 4, 'Should be 4'); + assert(new_arr.len() == 4, 'Len should be 4'); +} + +#[test] +#[available_gas(2000000)] +fn dedup_two_matches() { + let mut destination = array![1, 2, 2, 3, 4, 4]; + let new_arr = destination.dedup(); + let len = new_arr.len(); + + assert(*new_arr[0] == 1, 'Should be 1'); + assert(*new_arr[1] == 2, 'Should be 2'); + assert(*new_arr[2] == 3, 'Should be 3'); + assert(*new_arr[3] == 4, 'Should be 4'); + assert(new_arr.len() == 4, 'Len should be 4'); +} + +#[test] +#[available_gas(2000000)] +fn dedup_one_match_more() { + let mut destination = array![1, 2, 2, 2, 3, 4, 4]; + let new_arr = destination.dedup(); + let len = new_arr.len(); + + assert(*new_arr[0] == 1, 'Should be 1'); + assert(*new_arr[1] == 2, 'Should be 2'); + assert(*new_arr[2] == 3, 'Should be 3'); + assert(*new_arr[3] == 4, 'Should be 4'); + assert(new_arr.len() == 4, 'Len should be 4'); +} + +#[test] +#[available_gas(2000000)] +fn dedup_all_same() { + let mut destination = array![2, 2, 2, 2]; + let new_arr = destination.dedup(); + let len = new_arr.len(); + + assert(*new_arr[0] == 2, 'Should be 2'); + assert(new_arr.len() == 1, 'Len should be 1'); +} + +#[test] +#[available_gas(2000000)] +fn dedup_one_elem() { + let mut destination = array![2]; + let new_arr = destination.dedup(); + let len = new_arr.len(); + + assert(*new_arr[0] == 2, 'Should be 2'); + assert(new_arr.len() == 1, 'Len should be 1'); +} + +#[test] +#[available_gas(2000000)] +fn dedup_no_elem() { + let mut destination = ArrayTrait::::new(); + let new_arr = destination.dedup(); + let len = new_arr.len(); + assert(new_arr.len() == 0, 'Len should be 0'); +} + +#[test] +#[available_gas(2000000)] +fn dedup_multiple_duplicates_same() { + let mut destination = array![1, 1, 3, 4, 3, 3, 3, 4, 2, 2]; + let new_arr = destination.dedup(); + let len = new_arr.len(); + + assert(new_arr.len() == 6, 'Len should be 6'); + assert(*new_arr[0] == 1, 'Should be 1'); + assert(*new_arr[1] == 3, 'Should be 3'); + assert(*new_arr[2] == 4, 'Should be 4'); + assert(*new_arr[3] == 3, 'Should be 3'); + assert(*new_arr[4] == 4, 'Should be 4'); + assert(*new_arr[5] == 2, 'Should be 2'); +} + // append_all #[test]