Skip to content
Open
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
289 changes: 289 additions & 0 deletions solutions/c/react/1/react.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
#include "react.h"
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

struct cell_node {
struct cell *cell;
struct cell_node *next;
};

struct callback_node {
callback_id id;
void *obj;
callback func;
struct callback_node *next;
};

struct reactor {
struct cell_node *cells;
};

enum cell_type { INPUT_CELL, COMPUTE1_CELL, COMPUTE2_CELL };

struct cell{
int value;
int old_value;
enum cell_type type;

struct cell *parent1;
struct cell *parent2;

union {
compute1 fun1;
compute2 fun2;
} fun;

struct cell_node *dependents;
struct callback_node *callbacks;

bool changed;
};

struct reactor *create_reactor(void){
return calloc(1, sizeof(struct reactor));
}

void destroy_reactor(struct reactor *reactor) {
if (reactor) {
struct cell_node *node = reactor->cells;
while (node) {
struct callback_node *callback = node->cell->callbacks;
while (callback) {
struct callback_node *temp1 = callback->next;
free(callback);
callback = temp1;
}

struct cell_node *dependent = node->cell->dependents;
while (dependent) {
struct cell_node *temp1 = dependent->next;
free(dependent);
dependent = temp1;
}

struct cell_node *temp = node->next;

free(node->cell);
free(node);
node = temp;
}
free(reactor);
}
}

struct cell *create_input_cell(struct reactor *reactor, int initial_value){
if (!reactor) return NULL;

struct cell_node *new_node = malloc(sizeof(struct cell_node));
if (!new_node) return NULL;

struct cell *new_cell = calloc(1, sizeof(struct cell));
if (!new_cell) return NULL;

new_cell->type = INPUT_CELL;
new_cell->value = initial_value;

new_node->cell = new_cell;
new_node->next = reactor->cells;
reactor->cells = new_node;

return new_cell;
}

struct cell *create_compute1_cell(struct reactor *reactor, struct cell *parent, compute1 fun){
if (!reactor) return NULL;

struct cell_node *new_node = malloc(sizeof(struct cell_node));
if (!new_node) return NULL;

struct cell_node *new_dependent = malloc(sizeof(struct cell_node));
if (!new_dependent) return NULL;

struct cell *new_cell = calloc(1, sizeof(struct cell));
if (!new_cell) return NULL;

new_cell->type = COMPUTE1_CELL;
new_cell->fun.fun1 = fun;
new_cell->parent1 = parent;

new_cell->value = fun(parent->value);

new_dependent->cell = new_cell;
new_dependent->next = parent->dependents;
parent->dependents = new_dependent;

new_node->cell = new_cell;
new_node->next = reactor->cells;
reactor->cells = new_node;

return new_cell;
}

struct cell *create_compute2_cell(struct reactor *reactor, struct cell *parent1,
struct cell *parent2, compute2 fun){
if (!reactor) return NULL;

struct cell_node *new_node = malloc(sizeof(struct cell_node));
if (!new_node) return NULL;

struct cell_node *new_dependent_node1 = malloc(sizeof(struct cell_node));
if (!new_dependent_node1) return NULL;

struct cell_node *new_dependent_node2 = malloc(sizeof(struct cell_node));
if (!new_dependent_node2) return NULL;

struct cell *new_cell = calloc(1, sizeof(struct cell));
if (!new_cell) return NULL;

new_cell->type = COMPUTE2_CELL;
new_cell->fun.fun2 = fun;
new_cell->parent1 = parent1;
new_cell->parent2 = parent2;

new_cell->value = fun(parent1->value, parent2->value);

new_dependent_node1->cell = new_cell;
new_dependent_node2->cell = new_cell;

new_dependent_node1->next = parent1->dependents;
parent1->dependents = new_dependent_node1;

new_dependent_node2->next = parent2->dependents;
parent2->dependents = new_dependent_node2;

new_node->cell = new_cell;
new_node->next = reactor->cells;
reactor->cells = new_node;

return new_cell;
}

static inline bool update_value(struct cell *cell){
if (cell->type == INPUT_CELL) return false;

int new_value;
if (cell->type == COMPUTE1_CELL) {
new_value = cell->fun.fun1(cell->parent1->value);
} else {
new_value = cell->fun.fun2(cell->parent1->value, cell->parent2->value);
}

if (cell->value == new_value) return false;

cell->value = new_value;

return true;
}

int get_cell_value(struct cell *cell){
return cell->value;
}

static void push_dependents(struct cell_node **work, struct cell_node *dependents){
while (dependents) {
struct cell_node *node = malloc(sizeof(struct cell_node));
node->cell = dependents->cell;
node->next = *work;
*work = node;
dependents = dependents->next;
}
}

void set_cell_value(struct cell *cell, int new_value){
if (cell->type != INPUT_CELL || cell->value == new_value) {
return;
}

cell->value = new_value;

struct cell_node *work_list = NULL;
struct cell_node *changed_cells = NULL;

push_dependents(&work_list, cell->dependents);

while (work_list) {
struct cell_node *current_node = work_list;
struct cell *current_cell = current_node->cell;

work_list = current_node->next;
free(current_node);

int previous_value = current_cell->value;

if (update_value(current_cell)) {

if (!current_cell->changed) {
current_cell->changed = true;

current_cell->old_value = previous_value;

struct cell_node *changed_node = malloc(sizeof(struct cell_node));
changed_node->cell = current_cell;
changed_node->next = changed_cells;
changed_cells = changed_node;
}

push_dependents(&work_list, current_cell->dependents);
}
}

while (changed_cells) {
struct cell_node *current_node = changed_cells;
struct cell *current_cell = current_node->cell;

if (current_cell->value != current_cell->old_value) {
struct callback_node *callback = current_cell->callbacks;
while (callback) {
callback->func(callback->obj, current_cell->value);
callback = callback->next;
}
}

current_cell->changed = false;
changed_cells = current_node->next;
free(current_node);
}
}

// The callback should be called with the same void * given in add_callback.
callback_id add_callback(struct cell *cell, void *obj, callback callback){
static callback_id id = 0;

struct callback_node *new_node = calloc(1, sizeof(struct callback_node));
assert(new_node);

new_node->func = callback;
new_node->obj = obj;

new_node->next = cell->callbacks;
cell->callbacks = new_node;
new_node->id = id;

id++;
return new_node->id;
}

void remove_callback(struct cell *cell, callback_id id){
struct callback_node *callback = cell->callbacks;
if (!callback) return;

if (callback->id == id) {
cell->callbacks = callback->next;
free(callback);
return;
}

struct callback_node *next_callback = callback->next;

while (next_callback) {
if (next_callback->id == id) {
callback->next = next_callback->next;
free(next_callback);
return;
}

callback = next_callback;
next_callback = next_callback->next;
}
}
29 changes: 29 additions & 0 deletions solutions/c/react/1/react.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef REACT_H
#define REACT_H

struct reactor;
struct cell;

typedef int (*compute1)(int);
typedef int (*compute2)(int, int);

struct reactor *create_reactor(void);
// destroy_reactor should free all cells created under that reactor.
void destroy_reactor(struct reactor *);

struct cell *create_input_cell(struct reactor *, int initial_value);
struct cell *create_compute1_cell(struct reactor *, struct cell *, compute1);
struct cell *create_compute2_cell(struct reactor *, struct cell *,
struct cell *, compute2);

int get_cell_value(struct cell *);
void set_cell_value(struct cell *, int new_value);

typedef void (*callback)(void *, int);
typedef int callback_id;

// The callback should be called with the same void * given in add_callback.
callback_id add_callback(struct cell *, void *, callback);
void remove_callback(struct cell *, callback_id);

#endif