Skip to content

Commit

Permalink
Merge pull request #1 from Cumulo/test
Browse files Browse the repository at this point in the history
trying tests
  • Loading branch information
csvwolf authored Nov 9, 2024
2 parents 847cc1f + 2129978 commit 0ff04c7
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 59 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: check

on:
push:
branches:
- main
pull_request:

jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: install
run: |
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
echo "$HOME/.moon/bin" >> $GITHUB_PATH
# - name: moon check
# run: moon check

# - name: moon info
# run: |
# moon info
# git diff --exit-code

- name: moon test
run: |
moon test --target wasm-gc
# moon run src/main --target js
# - name: moon bundle
# run: moon bundle

# - name: check core size
# run: ls -alh `find ./target/bundle -name *.core`

# - name: format diff
# run: |
# moon fmt
# git diff
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# lexicon-fractional-index

_NOT YET_
> an implementation of [fractional index for lexicon](https://github.com/sh-tiye/lexicon-fractional-index/).
```bash
moon add tiye/lexicon-fractional-index
```

```moonbit
key_between(a, b) // generated key between a and b
n_key_between(a, b, n) // n generated keys between a and b
```

### License

Apache 2.0
6 changes: 3 additions & 3 deletions moon.mod.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "username/hello",
"version": "0.1.0",
"name": "tiye/lexicon-fractional-index",
"version": "0.0.2",
"readme": "README.md",
"repository": "",
"license": "Apache-2.0",
"keywords": [],
"description": "",
"source": "src"
}
}
45 changes: 30 additions & 15 deletions src/lib/key-between-test.mbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
fn keys_test() -> Unit! {
fn test_check(a : String?, b : String?, exp : String) -> Unit! {
let act = key_between!(a, b)
assert_eq!(exp, act)
match key_between?(a, b) {
Ok(act) => assert_eq!(exp, act)
Err(err) =>
match err {
KeyError(s) => assert_eq!(exp, s)
}
}
}

test_check!(None, None, "a0")
Expand Down Expand Up @@ -51,9 +56,13 @@ fn keys_test() -> Unit! {

fn test_n_keys() -> Unit! {
fn test_check(a : String?, b : String?, n : UInt, exp : String) -> Unit! {
let act_slice = n_keys_between!(a, b, n.reinterpret_as_int())
let act = String::concat(act_slice)
assert_eq!(exp, act)
match n_keys_between?(a, b, n.reinterpret_as_int()) {
Ok(act_slice) => {
let act = String::concat(act_slice, separator=" ")
assert_eq!(exp, act)
}
Err(err) => assert_eq!(exp, err.to_string())
}
}

test_check!(None, None, 5, "a0 a1 a2 a3 a4")
Expand All @@ -68,26 +77,32 @@ fn test_n_keys() -> Unit! {
}

fn test_to_float64_approx() -> Unit! {
let t_max = @int64.max_value
let t_max_f = t_max.to_double()
let epsilon = 1.0 / t_max_f
fn test_check(key : String, exp : Float, exp_err : String) -> Unit! {
let act = float64_approx!(key)
assert_true!((exp - act.to_float()).abs() < epsilon.to_float())
let epsilon = 1.0 / @double.max_value
fn test_check(key : String, exp : Double, exp_err : String) -> Unit! {
// println("Case: " + key + " " + exp.to_string())
match float64_approx?(key) {
Ok(act) =>
// println("act " + act.to_string() + " exp " + exp.to_string())
assert_true!((exp - act).abs() < epsilon)
Err(err) =>
match err {
KeyError(s) => assert_eq!(exp_err, s)
}
}
}

let n_62 : Float = 62.0
let n_62 = 62.0
test_check!("a0", 0.0, "")
test_check!("a1", 1.0, "")
test_check!("az", 61.0, "")
test_check!("b10", 62.0, "")
test_check!("z20000000000000000000000000", powf(n_62, 25) * 2.0, "")
// test_check!("z20000000000000000000000000", powf(n_62, 25) * 2.0, "")
test_check!("Z1", -1.0, "")
test_check!("Zz", -61.0, "")
test_check!("Y10", -62.0, "")
test_check!("A20000000000000000000000000", powf(n_62, 25) * -2.0, "")
// test_check!("A20000000000000000000000000", powf(n_62, 25) * -2.0, "")
test_check!("a0V", 0.5, "")
test_check!("a00V", (31.0 / powf(n_62, 2).to_double()).to_float(), "")
test_check!("a00V", 31.0 / powf(n_62, 2), "")
test_check!("aVV", 31.5, "")
test_check!("ZVV", -31.5, "")
test_check!("", 0.0, "invalid order key")
Expand Down
85 changes: 58 additions & 27 deletions src/lib/lib.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,51 @@ const SMALLEST_INT : String = "A00000000000000000000000000"

const ZERO : String = "a0"

type! KeyError String
/// common error structure of lexicon order key
pub type! KeyError String derive(Show)

/// compare them lexicographically, loop from head to tail, empty string is smallest
fn str_compare(a : String, b : String) -> Int {
let mut i = 0
while i < a.length() && i < b.length() {
let ca = a[i]
let cb = b[i]
if ca < cb {
return -1
}
if ca > cb {
return 1
}
i += 1
}
if a.length() < b.length() {
return -1
}
if a.length() > b.length() {
return 1
}
0
}

/// key_between returns a key that sorts lexicographically between a and b.
/// Either a or b can be empty strings. If a is empty it indicates smallest key,
/// If b is empty it indicates largest key.
/// b must be empty string or > a.
pub fn key_between(a : String?, b : String?) -> String!KeyError {
// println!("between: {} {}", a, b);
// println("between: " + a.to_string() + " " + b.to_string())
if not(a.is_empty()) {
validate_order_key!(a.unwrap())
}
if not(b.is_empty()) {
validate_order_key!(b.unwrap())
}
if not(a.is_empty()) && not(b.is_empty()) && a >= b {
if not(a.is_empty()) &&
not(b.is_empty()) &&
str_compare(a.unwrap(), b.unwrap()) >= 0 {
raise KeyError(
"invalid order: {} >= {}" +
"invalid order: " +
a.unwrap().to_string() +
" >= " +
b.unwrap().to_string(),
)
}
Expand All @@ -32,6 +59,7 @@ pub fn key_between(a : String?, b : String?) -> String!KeyError {
let int_b = get_int_part!(b.unwrap())
let float_part_b = b.unwrap().substring(start=int_b.length())
if int_b == SMALLEST_INT {
// println("midpoint 1 is: " + midpoint("", float_part_b))
return int_b + midpoint("", float_part_b)
}
if int_b < b.unwrap() {
Expand All @@ -48,6 +76,7 @@ pub fn key_between(a : String?, b : String?) -> String!KeyError {
let float_part_a = a.unwrap().substring(start=int_a.length())
let i = increment_int!(int_a)
if i.is_empty() {
// println("midpoint 2 is: " + midpoint(float_part_a, ""))
return int_a + midpoint(float_part_a, "")
}
return i
Expand All @@ -57,6 +86,7 @@ pub fn key_between(a : String?, b : String?) -> String!KeyError {
let int_b = get_int_part!(b.unwrap())
let float_part_b = b.unwrap().substring(start=int_b.length())
if int_a == int_b {
// println("midpoint 3 is: " + midpoint(float_part_a, float_part_b))
return int_a + midpoint(float_part_a, float_part_b)
}
let i = increment_int!(int_a)
Expand All @@ -74,6 +104,7 @@ pub fn key_between(a : String?, b : String?) -> String!KeyError {
/// b == "" means last possible string.
/// a, b MUST be str without head
fn midpoint(a : String, b : String) -> String {
// println("midpoint: " + a + " " + b)
if not(b.is_empty()) {
// remove longest common prefix. pad `a` with 0s as we
// go. note that we don't need to pad `b`, because it can't
Expand All @@ -82,7 +113,7 @@ fn midpoint(a : String, b : String) -> String {
for _i = 0; _i < a.length(); _i = _i + 1 {
let mut c : Char = '0'
if a.length() > i {
c = a.to_array()[i]
c = a[i]
}
if i >= b.length() || c != b[i] {
break
Expand All @@ -91,11 +122,10 @@ fn midpoint(a : String, b : String) -> String {
}
if i > 0 {
if i > a.length() - 1 {
return b.substring(end=i).to_string() +
midpoint("", b.substring(start=i))
return b.substring(end=i) + midpoint("", b.substring(start=i))
} else {
return b.substring(end=i) +
midpoint(a.substring(start=1), b.substring(start=i))
midpoint(a.substring(start=i), b.substring(start=i))
}
}
}
Expand All @@ -110,16 +140,17 @@ fn midpoint(a : String, b : String) -> String {
digit_b = BASE62_DIGITS.index_of(b[0].to_string())
}
if digit_b - digit_a > 1 {
let mid_digit = (0.5 * (digit_a + digit_b).to_double())
.round()
.reinterpret_as_int64()
.to_int()
// println(
// "DEBUG " + (0.5 * (digit_a + digit_b).to_double()).round().to_string(),
// )
let mid_digit = (0.5 * (digit_a + digit_b).to_double()).round().to_int()
// println("mid_digit: " + mid_digit.to_string())
return BASE62_DIGITS[mid_digit].to_string()
}

// first digits are consecutive
if b.length() > 1 {
if not(b.starts_with('0'.to_string())) {
if not(b.starts_with("0")) {
return b.substring(end=1)
}
return BASE62_DIGITS[digit_a].to_string() +
Expand All @@ -136,7 +167,7 @@ fn midpoint(a : String, b : String) -> String {
if not(a.is_empty()) {
sa = a.substring(start=1)
}
return BASE62_DIGITS.to_array()[digit_a].to_string() + midpoint(sa, "")
return BASE62_DIGITS[digit_a].to_string() + midpoint(sa, "")
}

fn validate_int(i : String) -> Unit!KeyError {
Expand Down Expand Up @@ -184,7 +215,7 @@ fn validate_order_key(key : String) -> Unit!KeyError {
let int_part = get_int_part!(key)
let float_part = key.substring(start=int_part.length())
if float_part.ends_with("0") {
raise KeyError("invalid order key:" + key)
raise KeyError("invalid order key: " + key)
}
}

Expand Down Expand Up @@ -214,8 +245,8 @@ fn increment_int(x : String) -> String!KeyError {
if head == 'z' {
return ""
}
let h = head.to_uint() + 1
if h > 'a'.to_uint() {
let h = Char::from_int(head.to_int() + 1)
if h > 'a' {
// a-z -> incr
digs.push('0')
} else {
Expand All @@ -238,23 +269,23 @@ fn decrement_int(x : String) -> String!KeyError {
while borrow && i >= 0 {
let d = BASE62_DIGITS.index_of(digs[i].to_string()) - 1
if d == -1 {
digs[i] = BASE62_DIGITS[-1]
digs[i] = BASE62_DIGITS[BASE62_DIGITS.length() - 1]
} else {
digs[i] = BASE62_DIGITS.to_array()[d]
digs[i] = BASE62_DIGITS[d]
borrow = false
}
i -= 1
}
if borrow {
if head == 'a' {
return "Z" + BASE62_DIGITS.to_array()[-1].to_string()
return "Z" + BASE62_DIGITS[BASE62_DIGITS.length() - 1].to_string()
}
if head == 'A' {
return ""
}
let h = head.to_uint() - 1
if h < 'Z'.to_uint() {
digs.push(BASE62_DIGITS.to_array()[-1])
let h = Char::from_int(head.to_int() - 1)
if h < 'Z' {
digs.push(BASE62_DIGITS.to_array()[BASE62_DIGITS.length() - 1])
} else {
let _v = digs.pop()

Expand Down Expand Up @@ -284,15 +315,15 @@ pub fn float64_approx(key : String) -> Double!KeyError {
if p < 0 {
raise KeyError("invalid order key: " + key)
}
rv += (pow(BASE62_DIGITS.length(), i) * p).to_float().to_double()
rv += (pow(BASE62_DIGITS.length(), i) * p).to_double()
}
let fp = key.substring(start=ip.length())
for i, d in fp.to_array() {
let p = BASE62_DIGITS.index_of(d.to_string())
if p < 0 {
raise KeyError("invalid key: " + key)
}
rv += (p / pow(BASE62_DIGITS.length(), i + 1)).to_double()
rv += p.to_double() / powf(BASE62_DIGITS.length().to_double(), i + 1)
}
if head < 'a' {
rv *= -1.0
Expand Down Expand Up @@ -364,8 +395,8 @@ pub fn pow(x : Int, exp : Int) -> Int {
result
}

pub fn powf(x : Float, exp : Int) -> Float {
let mut result : Float = 1.0
pub fn powf(x : Double, exp : Int) -> Double {
let mut result : Double = 1.0
for _i = 0; _i < exp; _i = _i + 1 {
result *= x
}
Expand Down
Loading

0 comments on commit 0ff04c7

Please sign in to comment.