Commit 0e6ef735 authored by Chris Müller's avatar Chris Müller
Browse files

update array implementation and add map support

parent e891415e
set(SOURCES
array.c)
array.c
red_black_tree.c
map.c
unicode.c)
add_library(cherry-core ${SOURCES})
......@@ -19,8 +19,6 @@
#include "standard.h"
typedef void (*cy_free_funptr)(pointer data);
#define CHERRY_OUT_OF_INDEX_ERROR -1
struct CyArray;
......
/*
* Cherry programming language
* Copyright (C) 2013 Christoph Mueller
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "map.h"
#include "red_black_tree.h"
#include <assert.h>
struct CyMap*
cy_map_new(cy_ordering_funptr compare)
{
return cy_cast(struct CyMap*, cy_rbtree_new(compare));
}
void
cy_map_clear(struct CyMap* map, cy_free_funptr fun_key, cy_free_funptr fun_data)
{
assert(map != 0);
cy_rbtree_clear(cy_cast(struct CyRbTree*, map), fun_key, fun_data);
}
size_t
cy_map_size(struct CyMap* map)
{
assert(map != 0);
return cy_cast(struct CyRbTree*, map)->nodes;
}
pointer
cy_map_lookup(struct CyMap* map, const_pointer key)
{
assert(map != 0);
struct CyRbNode* result = cy_rbtree_lookup(cy_cast(struct CyRbTree*, map), key);
return (result != 0) ? cy_cast(pointer, result->data) : 0;
}
int
cy_map_insert(struct CyMap* map, const_pointer key, const_pointer value)
{
assert(map != 0);
struct CyRbNode* result = cy_rbtree_insert(cy_cast(struct CyRbTree*, map), key, value);
return result != 0;
}
struct CyMapPair
cy_map_remove(struct CyMap* map, const_pointer key)
{
assert(map != 0);
struct CyMapPair pair = { .key = 0, .data = 0 };
struct CyRbNode* result = cy_rbtree_remove(cy_cast(struct CyRbTree*, map), key);
if(result) {
pair.key = result->entry;
pair.data = result->data;
cy_rbnode_free(result, 0, 0);
}
return pair;
}
/*
* Cherry programming language
* Copyright (C) 2013 Christoph Mueller
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "standard.h"
struct CyMapPair {
const_pointer key;
const_pointer data;
};
struct CyMap;
struct CyMap* cy_map_new(cy_ordering_funptr compare);
size_t cy_map_size(struct CyMap* map);
pointer cy_map_lookup(struct CyMap* map, const_pointer key);
int cy_map_insert(struct CyMap* map, const_pointer key, const_pointer value);
struct CyMapPair cy_map_remove(struct CyMap* map, const_pointer key);
void cy_map_clear(struct CyMap* map, cy_free_funptr fun_key, cy_free_funptr fun_data);
/*
* Cherry programming language
* Copyright (C) 2013 Christoph Mueller
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "red_black_tree.h"
struct CyRbTree*
cy_rbtree_new(cy_ordering_funptr compare)
{
assert(compare != 0);
struct CyRbTree* tree = cy_malloc(struct CyRbTree);
tree->root = 0;
tree->compare = compare;
tree->nodes = 0;
return tree;
}
struct CyRbNode*
cy_rbnode_new(enum CyRbNodeColor color, struct CyRbNode* parent, const_pointer entry, const_pointer data)
{
assert(entry != 0);
struct CyRbNode* node = cy_malloc(struct CyRbNode);
node->color = color;
node->parent = parent;
node->entry = entry;
node->data = data;
node->left = 0;
node->right = 0;
return node;
}
static cy_rbsubtree_free(struct CyRbNode* node, cy_free_funptr fun_entry, cy_free_funptr fun_data)
{
if(node == 0)
return;
cy_rbsubtree_free(node->left, fun_entry, fun_data);
cy_rbsubtree_free(node->right, fun_entry, fun_data);
cy_rbnode_free(node, fun_entry, fun_data);
}
void
cy_rbtree_clear(struct CyRbTree* tree, cy_free_funptr fun_entry, cy_free_funptr fun_data)
{
assert(tree != 0);
cy_rbsubtree_free(tree->root, fun_entry, fun_data);
tree->root = 0;
tree->nodes = 0;
}
void
cy_rbnode_free(struct CyRbNode* node, cy_free_funptr fun_entry, cy_free_funptr fun_data)
{
assert(node != 0);
if(fun_entry != 0)
fun_entry(cy_cast(pointer, node->entry));
if(fun_data != 0)
fun_data(cy_cast(pointer, node->data));
cy_free(node);
}
struct CyRbNode*
cy_rbtree_lookup(struct CyRbTree* tree, const_pointer entry)
{
assert(tree != 0 && entry != 0);
struct CyRbNode* node = tree->root;
while(node != 0) {
int result = tree->compare(entry, node->entry);
if(result < 0)
node = node->left;
else if(result > 0)
node = node->right;
else
return node;
}
return 0;
}
static struct CyRbNode*
cy_rbnode_rotate_left(struct CyRbNode* node)
{
assert(node != 0);
struct CyRbNode* parent = node->parent;
struct CyRbNode* right = node->right;
// switch right-node and its parent in a left-in-order rotation
node->right = right->left;
right->left = node;
// correct parent hierarchy
node->parent = right;
right->parent = parent;
if(node->right != 0)
node->right->parent = node;
if(parent != 0 && parent->left == node)
parent->left = right;
else if(parent != 0 && parent->right == node)
parent->right = right;
// return new sub_root
return right;
}
static struct CyRbNode*
cy_rbnode_rotate_right(struct CyRbNode* node)
{
assert(node != 0);
struct CyRbNode* parent = node->parent;
struct CyRbNode* left = node->left;
// switch left-node and its parent in a right-in-order rotation
node->left = left->right;
left->right = node;
// correct parent hierarchy
node->parent = left;
left->parent = parent;
if(node->left != 0)
node->left->parent = node;
if(parent != 0 && parent->left == node)
parent->left = left;
else if(parent != 0 && parent->right == node)
parent->right = left;
// return new sub_root
return left;
}
static struct CyRbNode*
cy_rbtrinode_restructering(struct CyRbNode* node)
{
assert(node != 0);
struct CyRbNode* root = RB_GRAND_PARENT(node);
if(root->left == RB_PARENT(node)) {
if(RB_PARENT(node)->left == node)
root = cy_rbnode_rotate_right(root);
else {
cy_rbnode_rotate_left(RB_PARENT(node));
root = cy_rbnode_rotate_right(root);
}
} else {
if(RB_PARENT(node)->right == node)
root = cy_rbnode_rotate_left(root);
else {
cy_rbnode_rotate_right(RB_PARENT(node));
root = cy_rbnode_rotate_left(root);
}
}
return root;
}
static void
cy_rbremedy_double_red(struct CyRbTree* tree, struct CyRbNode* node_z)
{
assert(tree != 0 && node_z != 0);
if(tree->root == RB_PARENT(node_z))
return;
else if(RB_PARENT(node_z)->color == BLACK)
return;
if(RB_GRAND_PARENT(node_z)->left == 0 || RB_GRAND_PARENT(node_z)->right == 0 || RB_GRAND_PARENT(node_z)->left->color == BLACK || RB_GRAND_PARENT(node_z)->right->color == BLACK) {
// trinode restructering with single/multiple left- and right rotations
struct CyRbNode* node_v = cy_rbtrinode_restructering(node_z);
node_v->color = BLACK;
node_v->left->color = RED;
node_v->right->color = RED;
while(node_v->parent != 0)
node_v = node_v->parent;
tree->root = node_v;
} else {
// recoloring of nodes
RB_GRAND_PARENT(node_z)->left->color = BLACK;
RB_GRAND_PARENT(node_z)->right->color = BLACK;
if(RB_GRAND_PARENT(node_z)->parent == 0)
return;
RB_GRAND_PARENT(node_z)->color = RED;
cy_rbremedy_double_red(tree, RB_GRAND_PARENT(node_z));
}
}
static void
cy_rbremedy_double_black(struct CyRbTree* tree, struct CyRbNode* node_x, struct CyRbNode* node_r)
{
assert(node_x != 0);
struct CyRbNode* sibling = (node_x->left == node_r) ? node_x->right : node_x->left;
struct CyRbNode* child = 0;
if(sibling == 0)
return;
enum CyRbNodeColor parent_color = node_x->color;
if(sibling->color == BLACK) {
// get red child of siblings if available
if(sibling->left != 0 && sibling->left->color == RED)
child = sibling->left;
else if(sibling->right != 0 && sibling->right->color == RED)
child = sibling->right;
if(child != 0) {
// Case 1: Restructering of siblings red child
child = cy_rbtrinode_restructering(child);
child->color = parent_color;
if(node_r != 0)
node_r->color = BLACK;
child->left->color = BLACK;
child->right->color = BLACK;
while(child->parent != 0)
child = child->parent;
tree->root = child;
} else {
// Case 2: Both siblings childs are black => Recoloring
if(node_r != 0)
node_r->color = BLACK;
sibling->color = RED;
if(node_x->color == RED)
node_x->color = BLACK;
else if(node_x->parent != 0) {
cy_rbremedy_double_black(tree, RB_PARENT(node_x), node_x);
}
}
} else {
// Case 3: Sibling is red => Adjustment needed
if(node_x->right == sibling)
child = sibling->right;
else
child = sibling->left;
if(child != 0) {
child = cy_rbtrinode_restructering(child);
sibling->color = BLACK;
node_x->color = RED;
while(child->parent != 0)
child = child->parent;
tree->root = child;
cy_rbremedy_double_black(tree, node_x, node_r);
}
}
}
struct CyRbNode*
cy_rbtree_insert(struct CyRbTree* tree, const_pointer entry, const_pointer data)
{
assert(tree != 0 && entry != 0);
struct CyRbNode* node = tree->root;
struct CyRbNode* current_node = 0;
if(node == 0) {
tree->root = cy_rbnode_new(BLACK, 0, entry, data);
tree->nodes += 1;
return tree->root;
}
// general node insertion for binary search tree
while(current_node == 0) {
int result = tree->compare(entry, node->entry);
if(result < 0) {
if(node->left == 0)
current_node = node->left = cy_rbnode_new(RED, node, entry, data);
else
node = node->left;
} else if(result > 0) {
if(node->right == 0)
current_node = node->right = cy_rbnode_new(RED, node, entry, data);
else
node = node->right;
} else {
return 0;
}
}
// repair binary search tree based on red-black tree properties
cy_rbremedy_double_red(tree, current_node);
tree->nodes += 1;
return current_node;
}
static struct CyRbNode*
cy_rbremove_external_node(struct CyRbNode* node)
{
assert(node != 0);
struct CyRbNode* child = (node->left == 0) ? node->right : node->left;
if(RB_PARENT(node) != 0 && RB_PARENT(node)->left == node)
RB_PARENT(node)->left = child;
else if(RB_PARENT(node) != 0)
RB_PARENT(node)->right = child;
if(child != 0)
child->parent = RB_PARENT(node);
return child;
}
struct CyRbNode*
cy_rbtree_remove(struct CyRbTree* tree, const_pointer entry)
{
assert(tree != 0 && entry != 0);
struct CyRbNode* node = 0;
struct CyRbNode* current_node = 0;
struct CyRbNode* data_node = 0;
// search for a node with the same entry within the binary tree
current_node = cy_rbtree_lookup(tree, entry);
if(current_node == 0)
return current_node;
else if(current_node->left != 0 && current_node->right != 0) {
// Find most-left internal node with an external child in the right subtree
node = current_node->right;
while(node->left != 0)
node = node->left;
// swap its entry information
const_pointer tmp_entry = node->entry;
const_pointer tmp_data = node->data;
node->entry = current_node->entry;
node->data = current_node->data;
current_node->entry = tmp_entry;
current_node->data = tmp_data;
data_node = node;
node = cy_rbremove_external_node(node);
} else {
// Remove internal node and replace it by its single subtree (Easy case)
data_node = current_node;
node = cy_rbremove_external_node(current_node);
}
// Node removed, now correct tree for red_black_tree properties
if(RB_PARENT(data_node) == 0) {
if(node != 0)
node->color = BLACK;
tree->root = node;
} else if(node != 0 && (node->color == RED || RB_PARENT(data_node)->color == RED)) {
node->color = BLACK;
} else {
cy_rbremedy_double_black(tree, RB_PARENT(data_node), node);
}
tree->nodes -= 1;
return data_node;
}
struct CyRbNode*
cy_rbnode_first(struct CyRbTree* tree)
{
assert(tree != 0);
if(tree->root == 0)
return 0;
struct CyRbNode* node = tree->root;
while(node->left != 0) {
node = node->left;
}
return node;
}
struct CyRbNode*
cy_rbnode_last(struct CyRbTree* tree)
{
assert(tree != 0);
if(tree->root == 0)
return 0;
struct CyRbNode* node = tree->root;
while(node->right != 0) {
node = node->right;
}
return node;
}
struct CyRbNode*
cy_rbnode_next(struct CyRbNode* node)
{
assert(node != 0);
struct CyRbNode* tmp = 0;
if(node->right != 0) {
tmp = node->right;
while(tmp->left != 0)
tmp = tmp->left;
return tmp;
}
tmp = node->parent;