Skip to content

Commit

Permalink
Added subset and superset operations for sets (#359)
Browse files Browse the repository at this point in the history
* Added subset testing

* Added set operations for superser and subset checking
  • Loading branch information
joente authored Jan 24, 2024
1 parent 3beb60d commit 29463c8
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* libcleri is now integrated into the core ThingsDB codebase, eliminating the need for separate installation.
* The `new_backup()` and `new_token()` functions no longer accept `int`, `float` or `str` as time.
_(This was marked as deprecated since v0.10.1)_
* Added set operators `<`, `<=`, `>`, `>=` for subset, proper subset, superset and proper superset checking.

# v1.4.16

Expand Down
3 changes: 3 additions & 0 deletions inc/ti/opr/ge.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ static int opr__ge(ti_val_t * a, ti_val_t ** b, ex_t * e)
case OPR_BYTES_BYTES:
bool_ = ti_raw_cmp((ti_raw_t *) a, (ti_raw_t *) *b) >= 0;
break;
case OPR_SET_SET:
bool_ = ti_vset_ge((ti_vset_t*) a, (ti_vset_t *) *b);
break;
}

ti_val_unsafe_drop(*b);
Expand Down
3 changes: 3 additions & 0 deletions inc/ti/opr/gt.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ static int opr__gt(ti_val_t * a, ti_val_t ** b, ex_t * e)
case OPR_BYTES_BYTES:
bool_ = ti_raw_cmp((ti_raw_t *) a, (ti_raw_t *) *b) > 0;
break;
case OPR_SET_SET:
bool_ = ti_vset_gt((ti_vset_t*) a, (ti_vset_t *) *b);
break;
}

ti_val_unsafe_drop(*b);
Expand Down
3 changes: 3 additions & 0 deletions inc/ti/opr/le.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ static int opr__le(ti_val_t * a, ti_val_t ** b, ex_t * e)
case OPR_BYTES_BYTES:
bool_ = ti_raw_cmp((ti_raw_t *) a, (ti_raw_t *) *b) <= 0;
break;
case OPR_SET_SET:
bool_ = ti_vset_le((ti_vset_t*) a, (ti_vset_t *) *b);
break;
}

ti_val_unsafe_drop(*b);
Expand Down
3 changes: 3 additions & 0 deletions inc/ti/opr/lt.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ static int opr__lt(ti_val_t * a, ti_val_t ** b, ex_t * e)
case OPR_BYTES_BYTES:
bool_ = ti_raw_cmp((ti_raw_t *) a, (ti_raw_t *) *b) < 0;
break;
case OPR_SET_SET:
bool_ = ti_vset_lt((ti_vset_t*) a, (ti_vset_t *) *b);
break;
}

ti_val_unsafe_drop(*b);
Expand Down
2 changes: 1 addition & 1 deletion inc/ti/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
* "-rc0"
* ""
*/
#define TI_VERSION_PRE_RELEASE "-alpha8"
#define TI_VERSION_PRE_RELEASE "-alpha9"

#define TI_MAINTAINER \
"Jeroen van der Heijden <jeroen@cesbit.com>"
Expand Down
20 changes: 20 additions & 0 deletions inc/ti/vset.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,26 @@ static inline _Bool ti_vset_eq(ti_vset_t * va, ti_vset_t * vb)
return imap_eq(va->imap, vb->imap);
}

static inline _Bool ti_vset_le(ti_vset_t * va, ti_vset_t * vb)
{
return imap_le(va->imap, vb->imap);
}

static inline _Bool ti_vset_lt(ti_vset_t * va, ti_vset_t * vb)
{
return imap_lt(va->imap, vb->imap);
}

static inline _Bool ti_vset_ge(ti_vset_t * va, ti_vset_t * vb)
{
return imap_le(vb->imap, va->imap);
}

static inline _Bool ti_vset_gt(ti_vset_t * va, ti_vset_t * vb)
{
return imap_lt(vb->imap, va->imap);
}

static inline void * ti_vset_key(ti_vset_t * vset)
{
return ti_thing_is_object(vset->parent)
Expand Down
11 changes: 11 additions & 0 deletions inc/util/imap.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ int imap_walk_cp(
imap_destroy_cb destroy_cb);
void imap_walkn(imap_t * imap, size_t * n, imap_cb cb, void * arg);
_Bool imap__eq_(imap_t * a, imap_t * b);
_Bool imap__le_(imap_t * a, imap_t * b);
static inline _Bool imap_eq(imap_t * a, imap_t * b);
vec_t * imap_vec(imap_t * imap);
vec_t * imap_vec_ref(imap_t * imap);
Expand Down Expand Up @@ -83,4 +84,14 @@ static inline _Bool imap_eq(imap_t * a, imap_t * b)
return a == b || (a->n == b->n && (!a->n || imap__eq_(a, b)));
}

static inline _Bool imap_le(imap_t * a, imap_t * b)
{
return a == b || !a->n || (a->n <= b->n && imap__le_(a, b));
}

static inline _Bool imap_lt(imap_t * a, imap_t * b)
{
return (!a->n && b->n) || (a != b && a->n < b->n && imap__le_(a, b));
}

#endif /* IMAP_H_ */
12 changes: 0 additions & 12 deletions itest/test_advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -2025,18 +2025,6 @@ async def test_reserved_enum_union(self, client):
'name `union` is reserved'):
await client.query('new_type("union");')

async def test_reserved_enum_union(self, client):
# bug #294
with self.assertRaisesRegex(
ValueError,
'name `enum` is reserved'):
await client.query('new_type("enum");')

with self.assertRaisesRegex(
ValueError,
'name `union` is reserved'):
await client.query('new_type("union");')

async def test_loop_set_relation_error(self, client):
# bug 302
with self.assertRaises(AssertionError):
Expand Down
29 changes: 29 additions & 0 deletions itest/test_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,35 @@ async def test_set_operations(self, client):
''')
self.assertEqual(set(res), {10, 20, 30, 41, 42, 50})

res = await client.query(r"""//ti
[
set() < set(),
set() <= set(),
set() < set(.x10),
set(.x10, .x40, .x41) < set(.x10, .x40, .x41),
set(.x10, .x40, .x41) <= set(.x10, .x40, .x41),
set(.x10, .x40, .x41) > set(.x10, .x40, .x41),
set(.x10, .x40, .x41) >= set(.x10, .x40, .x41),
set(.x10) <= set(),
set(.x10) < set(),
set(.x10, .x40, .x41) < set(.x10, .x41, .x42, .x43),
set(.x10, .x41) <= set(.x10, .x11, .x41, .x42, .x43),
];
""")
self.assertEqual(res, [
False,
True,
True,
False,
True,
False,
True,
False,
False,
False,
True,
])

async def test_preopr(self, client):
self.assertIs(await client.query(r'''
!true;
Expand Down
72 changes: 72 additions & 0 deletions src/util/imap.c
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,57 @@ static _Bool imap__eq(imap_node_t * nodea, imap_node_t * nodeb)
}
}

static _Bool imap__le(imap_node_t * nodea, imap_node_t * nodeb)
{
if (nodea->key == nodeb->key)
{
imap_node_t
* nda = nodea->nodes,
* ndb = nodeb->nodes,
* end = nda + imap__node_size(nodea);

for (; nda < end; ++nda, ++ndb)
if (nda->sz > ndb->sz ||
(nda->data && !ndb->data) ||
(nda->nodes && !ndb->nodes) ||
(nda->nodes && !imap__le(nda, ndb)))
return false;
return true;
}

if (nodea->key != IMAP_NODE_SZ && nodeb->key != IMAP_NODE_SZ)
return false;

if (nodeb->key == IMAP_NODE_SZ)
{
imap_node_t
* nda = nodea->nodes,
* ndb = nodeb->nodes + nodea->key;
return !(nda->sz > ndb->sz ||
(nda->data && !ndb->data) ||
(nda->nodes && !ndb->nodes) ||
(nda->nodes && !imap__le(nda, ndb)));
}
else
{
uint8_t key = 0;
imap_node_t
* nda = nodea->nodes,
* ndb = nodeb->nodes,
* end = nda + IMAP_NODE_SZ;

for (; nda < end; ++nda, ++key)
if ((nodeb->key == key && (
nda->sz > ndb->sz ||
(nda->data && !ndb->data) ||
(nda->nodes && !ndb->nodes) ||
(nda->nodes && !imap__le(nda, ndb))
)) || (nodeb->key != key && nda->sz))
return false;
return true;
}
}

/*
* Returns `true` if the given imap objects are equal
*/
Expand All @@ -581,6 +632,27 @@ _Bool imap__eq_(imap_t * a, imap_t * b)
return true;
}

/*
* Returns `true` if the given imap objects are equal
*/
_Bool imap__le_(imap_t * a, imap_t * b)
{
imap_node_t
* nda = a->nodes,
* ndb = b->nodes,
* end = nda + IMAP_NODE_SZ;

assert(a != b && a->n <= b->n && a->n);

for (; nda < end; ++nda, ++ndb)
if ((nda->data && !ndb->data) ||
(nda->nodes && !ndb->nodes) ||
(nda->nodes && !imap__le(nda, ndb)))
return false;

return true;
}

static void imap__vec(imap_node_t * node, vec_t * vec)
{
imap_node_t * nd = node->nodes, * end = nd + imap__node_size(node);
Expand Down

0 comments on commit 29463c8

Please sign in to comment.