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

[FEAT]: Implement an array sorting script #116

Merged
merged 11 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions cairo/scripts/sorting/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
.snfoundry_cache/
2 changes: 2 additions & 0 deletions cairo/scripts/sorting/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
scarb 2.9.2
snforge 0.31.0
73 changes: 73 additions & 0 deletions cairo/scripts/sorting/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "alexandria_bytes"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3"
dependencies = [
"alexandria_data_structures",
"alexandria_math",
]

[[package]]
name = "alexandria_data_structures"
version = "0.2.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3"
dependencies = [
"alexandria_encoding",
]

[[package]]
name = "alexandria_encoding"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3"
dependencies = [
"alexandria_bytes",
"alexandria_data_structures",
"alexandria_math",
"alexandria_numeric",
]

[[package]]
name = "alexandria_math"
version = "0.2.1"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3"

[[package]]
name = "alexandria_numeric"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3"
dependencies = [
"alexandria_math",
"alexandria_searching",
]

[[package]]
name = "alexandria_searching"
version = "0.1.0"
source = "git+https://github.com/keep-starknet-strange/alexandria.git#162bed1c636d31ccaaa90ed3eb32c9eb1d5e3bd3"
dependencies = [
"alexandria_data_structures",
]

[[package]]
name = "snforge_scarb_plugin"
version = "0.31.0"
source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67"

[[package]]
name = "snforge_std"
version = "0.31.0"
source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.31.0#72ea785ca354e9e506de3e5d687da9fb2c1b3c67"
dependencies = [
"snforge_scarb_plugin",
]

[[package]]
name = "sorting"
version = "0.1.0"
dependencies = [
"alexandria_data_structures",
"snforge_std",
]
20 changes: 20 additions & 0 deletions cairo/scripts/sorting/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "sorting"
version = "0.1.0"
edition = "2023_11"

# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html

[dependencies]
starknet = "2.9.2"
alexandria_data_structures = { git = "https://github.com/keep-starknet-strange/alexandria.git" }

[dev-dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.31.0" }
assert_macros = "2.9.2"

[[target.starknet-contract]]
sierra = true

[scripts]
test = "snforge test"
9 changes: 9 additions & 0 deletions cairo/scripts/sorting/snfoundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html for more information

# [sncast.myprofile1] # Define a profile name
# url = "http://127.0.0.1:5050/" # Url of the RPC provider
# accounts_file = "../account-file" # Path to the file with the account data
# account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions
# keystore = "~/keystore" # Path to the keystore file
# wait_params = { timeout = 500, retry_interval = 10 } # Wait for submitted transaction parameters
# block_explorer = "StarkScan" # Block explorer service used to display links to transaction details
48 changes: 48 additions & 0 deletions cairo/scripts/sorting/src/bubble_sort.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//! Bubble sort algorithm

// Bubble sort
/// # Arguments
/// * `array` - Array to sort
/// # Returns
/// * `Array<usize>` - Sorted array
use super::interface::Sortable;

pub impl BubbleSort of Sortable {
fn sort<T, +Copy<T>, +Drop<T>, +PartialOrd<T>>(mut array: Span<T>) -> Array<T> {
if array.len() == 0 {
return array![];
}
if array.len() == 1 {
return array![*array[0]];
}
let mut idx1 = 0;
let mut idx2 = 1;
let mut sorted_iteration = true;
let mut sorted_array = array![];

loop {
if idx2 == array.len() {
sorted_array.append(*array[idx1]);
if sorted_iteration {
break;
}
array = sorted_array.span();
sorted_array = array![];
idx1 = 0;
idx2 = 1;
sorted_iteration = true;
} else {
if *array[idx1] <= *array[idx2] {
sorted_array.append(*array[idx1]);
idx1 = idx2;
idx2 += 1;
} else {
sorted_array.append(*array[idx2]);
idx2 += 1;
sorted_iteration = false;
}
};
};
sorted_array
}
}
11 changes: 11 additions & 0 deletions cairo/scripts/sorting/src/interface.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use alexandria_data_structures::vec::{Felt252Vec};

pub trait Sortable {
fn sort<T, +Copy<T>, +Drop<T>, +PartialOrd<T>>(array: Span<T>) -> Array<T>;
}

pub trait SortableVec {
fn sort<T, +Copy<T>, +Drop<T>, +PartialOrd<T>, +Felt252DictValue<T>>(
array: Felt252Vec<T>,
) -> Felt252Vec<T>;
}
5 changes: 5 additions & 0 deletions cairo/scripts/sorting/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod bubble_sort;
pub mod merge_sort;
pub mod quick_sort;
pub mod interface;
pub use interface::{Sortable, SortableVec};
73 changes: 73 additions & 0 deletions cairo/scripts/sorting/src/merge_sort.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Merge Sort

// Merge Sort
/// # Arguments
/// * `arr` - Array to sort
/// # Returns
/// * `Array<T>` - Sorted array

use super::interface::Sortable;

pub impl MergeSort of Sortable {
fn sort<T, +Copy<T>, +Drop<T>, +PartialOrd<T>>(mut array: Span<T>) -> Array<T> {
let len = array.len();
if len == 0 {
return array![];
}
if len == 1 {
return array![*array[0]];
}

// Create left and right arrays
let middle = len / 2;
let left_arr = array.slice(0, middle);
let right_arr = array.slice(middle, len - middle);

// Recursively sort the left and right arrays
let sorted_left = Self::sort(left_arr);
let sorted_right = Self::sort(right_arr);

let mut result_arr = array![];
merge_recursive(sorted_left, sorted_right, ref result_arr, 0, 0);
result_arr
}
}

// Merge two sorted arrays
/// # Arguments
/// * `left_arr` - Left array
/// * `right_arr` - Right array
/// * `result_arr` - Result array
/// * `left_arr_ix` - Left array index
/// * `right_arr_ix` - Right array index
/// # Returns
/// * `Array<usize>` - Sorted array
fn merge_recursive<T, +Copy<T>, +Drop<T>, +PartialOrd<T>>(
mut left_arr: Array<T>,
mut right_arr: Array<T>,
ref result_arr: Array<T>,
left_arr_ix: usize,
right_arr_ix: usize,
) {
if result_arr.len() == left_arr.len() + right_arr.len() {
return;
}

if left_arr_ix == left_arr.len() {
result_arr.append(*right_arr[right_arr_ix]);
return merge_recursive(left_arr, right_arr, ref result_arr, left_arr_ix, right_arr_ix + 1);
}

if right_arr_ix == right_arr.len() {
result_arr.append(*left_arr[left_arr_ix]);
return merge_recursive(left_arr, right_arr, ref result_arr, left_arr_ix + 1, right_arr_ix);
}

if *left_arr[left_arr_ix] < *right_arr[right_arr_ix] {
result_arr.append(*left_arr[left_arr_ix]);
merge_recursive(left_arr, right_arr, ref result_arr, left_arr_ix + 1, right_arr_ix)
} else {
result_arr.append(*right_arr[right_arr_ix]);
merge_recursive(left_arr, right_arr, ref result_arr, left_arr_ix, right_arr_ix + 1)
}
}
61 changes: 61 additions & 0 deletions cairo/scripts/sorting/src/quick_sort.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! Quick sort algorithm
use alexandria_data_structures::vec::{Felt252Vec, VecTrait};

// Quick sort
/// # Arguments
/// * `Felt252Vec<T>` - Array to sort
/// # Returns
/// * `Felt252Vec<T>` - Sorted array

use super::SortableVec;

pub impl QuickSort of SortableVec {
fn sort<T, +Copy<T>, +Drop<T>, +PartialOrd<T>, +Felt252DictValue<T>>(
mut array: Felt252Vec<T>,
) -> Felt252Vec<T> {
let array_size = array.len();
if array_size <= 1 {
return array;
}
quick_sort_range(ref array, 0, array_size - 1);

return array;
}
}

fn quick_sort_range<T, +Copy<T>, +Drop<T>, +PartialOrd<T>, +Felt252DictValue<T>>(
ref array: Felt252Vec<T>, left: usize, right: usize,
) {
if left >= right {
return;
}

let mut l = left;
let mut r = right;

while l < r {
while (l < r) && (array[r] >= array[left]) {
r -= 1;
};

while (l < r) && (array[l] <= array[left]) {
l += 1;
};

if left != right {
let tmp = array[l];
array.set(l, array[r]);
array.set(r, tmp);
}
};

let tmp = array[left];
array.set(left, array[l]);
array.set(l, tmp);

if l > 1 {
quick_sort_range(ref array, left, l - 1);
}

quick_sort_range(ref array, r + 1, right);
}
30 changes: 30 additions & 0 deletions cairo/scripts/sorting/tests/test_bubble_sort.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use core::array::ArrayTrait;
use sorting::bubble_sort::BubbleSort;

#[test]
fn test_empty_array() {
let empty: Array<u32> = array![];
let result = BubbleSort::sort(empty.span());
assert!(result.len() == 0, "Empty array test failed");
}

#[test]
fn test_single_element() {
let single = array![1_u32];
let result = BubbleSort::sort(single.span());
assert!(result.len() == 1 && *result[0] == 1_u32, "Single element test failed");
}

#[test]
fn test_unsorted_array() {
let unsorted = array![3_u32, 1_u32, 4_u32, 1_u32, 5_u32];
let result = BubbleSort::sort(unsorted.span());
assert!(
*result[0] == 1_u32
&& *result[1] == 1_u32
&& *result[2] == 3_u32
&& *result[3] == 4_u32
&& *result[4] == 5_u32,
"Sorting test failed",
);
}
Loading