Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Feb 6th, 2008, 6:03 PM   #1
Sane
Banned
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 2,101
Rep Power: 6 Sane will become famous soon enough
Send a message via MSN to Sane
S&K Trees, Differences In Code And Speed

The problem statement where the solution comes from is not exactly important. But if you want to see where the code is coming from, here is the question:

http://cemc.math.uwaterloo.ca/ccc/2004/stage2/sk/sk.pdf

I found a solution online that is very, very fast.

C Syntax (Toggle Plain Text)
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4.  
  5. int debug = 0;
  6.  
  7. typedef struct _ {
  8. int rc;
  9. struct _ *l;
  10. struct _ *r;
  11. } nd;
  12.  
  13. nd* S = ((nd*) 1);
  14. nd* K = ((nd*) 2);
  15.  
  16. static int isleaf( nd* n ) {
  17. return ((unsigned)n) < 3;
  18. }
  19.  
  20. static void decref( nd* n ) {
  21. if( isleaf(n) ) return;
  22. n->rc--;
  23. if( !(n->rc) ) {
  24. decref(n->l);
  25. decref(n->r);
  26. free(n);
  27. }
  28. }
  29.  
  30. static void incref( nd* n ) {
  31. if( !isleaf(n) ) n->rc++;
  32. }
  33.  
  34. static void assign( nd** dst, nd* src ) {
  35. incref(src);
  36. decref(*dst);
  37. *dst = src;
  38. }
  39.  
  40. static nd* node(nd* l, nd* r) {
  41. //debug++;
  42. nd* ret = (nd*) malloc( sizeof(nd) );
  43. memset(ret,0,sizeof(nd));
  44. assign( &(ret->l), l );
  45. assign( &(ret->r), r );
  46. return ret;
  47. }
  48.  
  49. static nd* parse(char** buf) {
  50. nd *ret, *l, *r;
  51. switch(**buf) {
  52. case 'S': ret = S; break;
  53. case 'K': ret = K; break;
  54. case '(':
  55. (*buf)++;
  56. l = parse(buf);
  57. r = parse(buf);
  58. ret = node(l,r);
  59. break;
  60. }
  61. (*buf)++;
  62. return ret;
  63. }
  64.  
  65. static nd* eval(nd* n) {
  66. //debug++;
  67. nd* ret;
  68. if( isleaf(n) ) return NULL;
  69. if( isleaf(n->l) ) goto rule3;
  70. rule1:
  71. if( n->l->l == K ) return n->l->r;
  72. if( isleaf(n->l->l) ) goto rule3;
  73. rule2:
  74. if( n->l->l->l == S ) return node(node(n->l->l->r, n->r), node(n->l->r, n->r));
  75. rule3:
  76. if( ret = eval( n->l ) ) return node(ret, n->r);
  77. rule4:
  78. if( ret = eval( n->r ) ) return node(n->l, ret);
  79. rule5:
  80. return NULL;
  81. }
  82.  
  83. static void print( nd* n ) {
  84. if( n == S ) printf("S");
  85. else if( n == K ) printf("K");
  86. else {
  87. printf("(");
  88. print( n->l );
  89. print( n->r );
  90. printf(")");
  91. }
  92. }
  93. main() {
  94. freopen("d1_3.in", "rt", stdin);
  95. freopen("d1_3.out", "wt", stdout);
  96.  
  97. char buf[1024*64];
  98. char* c;
  99. nd* tree = NULL;
  100. nd* oldtree = NULL;
  101. while(1) {
  102. gets(buf);
  103. if( !strlen(buf) ) break;
  104. c = buf;
  105. assign( &tree, parse(&c) );
  106. while(1) {
  107. //print(tree);
  108. //printf("\n");
  109. assign( &oldtree, tree );
  110. assign( &tree, eval(tree) );
  111. if( !tree ) break;
  112. }
  113. print(oldtree);
  114. //printf("\n\n%d\n", debug);
  115. }
  116. }

It takes a few milleseconds for the following input file:

(((S((S((SK)K))(K(S((S(KS))K)))))(K(K((SK)K))))((((S((S((S(KS))K))(K((S((SK)K))((SK)K)))))((S((S(KS))K))(K((S((SK)K))((SK)K)))))((S(K(S((S((S((S((SK)K))(K(K(K((SK)K))))))(KK)))(K((S((S(KS))K))(K((SK)K))))))))((S(K(S((S(KS))K))))((S((S(KS))K))(K((S(K(S(K(S(K((S((SK)K))(K(K((SK)K))))))))))((S((S(KS))((S(K(S(KS))))((S(K(S(KK))))((S((S(KS))K))(K((S((S(KS))((S(K(S(K((S((S(KS))((S(KK))((S(KS))((S(K(S((SK)K))))K)))))(KK))))))((S((S(KS))K))(K((S((SK)K))(KK)))))))(K((S((SK)K))(KK))))))))))(K(K((S((S((S(KS))((S(KK))((S(KS))((S(K(S((SK)K))))K)))))(KK)))((SK)K)))))))))))((S((S(KS))K))((S((S(KS))K))((S((S(KS))K))(K((SK)K)))))))

My solution was very similar:

C Syntax (Toggle Plain Text)
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. int debug = 0;
  6.  
  7. typedef struct self_node {
  8. struct self_node *l;
  9. struct self_node *r;
  10. } t_node;
  11.  
  12. t_node t_s = { NULL };
  13. t_node t_k = { NULL };
  14.  
  15. t_node *new_node(t_node *l, t_node *r) {
  16. //debug++;
  17. t_node (* node) = (t_node *)malloc(sizeof(t_node));
  18. node->l = l;
  19. node->r = r;
  20. return node;
  21. }
  22.  
  23. t_node *parse(char **s) {
  24. t_node *node;
  25.  
  26. if (**s == '(') {
  27. (*s)++;
  28. node = parse(s);
  29. node = new_node(node, parse(s));
  30. }
  31. else if (**s == 'S') node = &t_s;
  32. else if (**s == 'K') node = &t_k;
  33.  
  34. (*s)++;
  35. return node;
  36. }
  37.  
  38. void print(t_node *node) {
  39. if(!node)return;
  40. if(node == &t_s) {
  41. printf("S");
  42. return;
  43. }
  44. if(node == &t_k) {
  45. printf("K");
  46. return;
  47. }
  48. printf("(");
  49. print(node->l);
  50. print(node->r);
  51. printf(")");
  52. }
  53.  
  54. int isleaf(t_node *node) {
  55. return (node == &t_s) || (node == &t_k);
  56. }
  57.  
  58. void freetree(t_node *node) {
  59. if (!node || isleaf(node)) return;
  60. freetree(node->l);
  61. freetree(node->r);
  62. free(node);
  63. }
  64.  
  65. t_node *clonetree(t_node *node){
  66. if (!node || isleaf(node)) return node;
  67. return new_node(clonetree(node->l), clonetree(node->r));
  68. }
  69.  
  70. t_node *eval(t_node *node) {
  71. //debug++;
  72. // Base Case
  73. if(!node || isleaf(node)) return NULL;
  74.  
  75. t_node *temp;
  76.  
  77. // Rule 1
  78. if(node->l &&
  79. node->l->l == &t_k) {
  80.  
  81. temp = node->l->r;
  82. freetree(node->r);
  83. free(node->l);
  84. node = temp;
  85. return node;
  86. }
  87.  
  88. // Rule 2
  89. if(node->l &&
  90. node->l->l &&
  91. node->l->l->l == &t_s) {
  92.  
  93. temp = node->l->l->r;
  94. free(node->l->l);
  95. node->l->l = temp;
  96. temp = node->l->r;
  97. node->l->r = node->r;
  98.  
  99. node->r = new_node(temp, clonetree(node->l->r));
  100. return node;
  101. }
  102.  
  103. // Rule 3
  104. temp = eval(node->l);
  105. if(temp) {
  106. node->l = temp;
  107. return node;
  108. }
  109.  
  110. // Rule 4
  111. temp = eval(node->r);
  112. if(temp) {
  113. node->r = temp;
  114. return node;
  115. }
  116.  
  117. // Rule 5
  118. return NULL;
  119. }
  120.  
  121. int main() {
  122.  
  123. char buff[1024*64];
  124. char *c;
  125.  
  126. t_node *tree = NULL;
  127. t_node *temp;
  128.  
  129. freopen("d1_3.in", "rt", stdin);
  130. freopen("d1_3.out", "wt", stdout);
  131.  
  132. gets(buff);
  133. while(strlen(buff)) {
  134. c = buff;
  135. tree = parse(&c);
  136. while (1) {
  137. //print(tree);
  138. //printf("\n");
  139.  
  140. temp = eval(tree);
  141. if (temp == NULL) break;
  142. tree = temp;
  143. }
  144. print(tree);
  145. //printf("\n\n%d\n", debug);
  146. freetree(tree);
  147. gets(buff);
  148. }
  149. return 0;
  150. }

However, there's one huge difference. Mine takes 30 seconds to arrive at the same answer, where his takes 0.01 seconds. What in the world is going on here?

I can't wrap my head around the magic behind the "node" function in the first code. That is where the speed increase is occuring. It somehow limits the number of times it calls malloc.

I set a counter to keep track of how many times malloc is called in their code: 65079. In my code, I call malloc 20525615 times.

But, now for the weird part: Our functions "eval" are called the same number of times in both versions: 217237 times. The node function is the only difference between memory allocation. And the node function (which calls malloc) is used more often in his eval (5 times) than in my eval (1 time). According to that, his function is calling malloc more than mine. It doesn't make any sense.

It must be his incref, decref, and assign, that effectively reduce the number of calls to malloc. It somehow makes it so he does not need to call "clonetree" as I have done. I have a feeling that's where my large number of mallocs come from. But I don't see how or why his approach works.

Question:
Could anyone explain to me how incref, decref, and assign all work, in context of the tree?
Sane is offline   Reply With Quote
Old Feb 7th, 2008, 12:48 AM   #2
Sane
Banned
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 2,101
Rep Power: 6 Sane will become famous soon enough
Send a message via MSN to Sane
Re: S&K Trees, Differences In Code And Speed

Update: So I set a counter on the clonetree function, and it turns out it accounts for 20521777 / 20525615 of the mallocs. In other words, it's taking up 99.98% of the run time. Therefore, the call to clonetree (in my eval) is the key difference between our two algorithms.

But if I address the memory, instead of copying it with clonetree, it does not work. It must be copied. But for some reason, the first implementation's use of incref and decref creates a way around needing to copy it. Can anyone decipher that code and figure out why he does not need to recursively copy the subtree?

I know this must be a hassle for anyone to commit time to look at it closely. So I greatly appreciate anyone's attempts to help me understand this. It's been bugging me for a while now.
Sane is offline   Reply With Quote
Old Feb 7th, 2008, 1:53 AM   #3
The Dark
Expert Programmer
 
Join Date: Jun 2005
Posts: 894
Rep Power: 4 The Dark is on a distinguished road
Re: S&K Trees, Differences In Code And Speed

Because the subtrees are never modified directly (i.e. they are only used to create new subtrees, or they are 'decref'ed - their l and r nodes are never modified), it is safe to have two subtrees pointing to the same memory. The tricky part is that obviously you can't free something that is still in use. That is where the reference counting comes in. Whenever a node is moved from one location to another, the old location is decref'ed and the new location is incref'ed.

Note also that this implementation will return true for isLeaf(NULL), since NULL < 3 - it took me a while to spot that. That prevents NULL pointers from being increfed or decrefed.

You could try this with your implementation - just change clonenode to just return the original value and remove the free call from freetree. Sure it will leak memory like a sieve, but you should end up with comparable speed, assuming 64000 allocations with no frees don't muck up your runtime.
The Dark is offline   Reply With Quote
Old Feb 7th, 2008, 4:44 PM   #4
Sane
Banned
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 2,101
Rep Power: 6 Sane will become famous soon enough
Send a message via MSN to Sane
Re: S&K Trees, Differences In Code And Speed

Thanks for your reply. I tried your suggestion in the three following ways. None of them worked. They all blink, and then produce a 0kb output file.

Commented Clonetree and Freetree:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int debug = 0;

typedef struct self_node {
    struct self_node *l;
    struct self_node *r;
} t_node;

t_node t_s = { NULL };
t_node t_k = { NULL };

t_node *new_node(t_node *l, t_node *r) {
    //debug++;
    t_node (* node) = (t_node *)malloc(sizeof(t_node));
    node->l = l;
    node->r = r;
    return node;
}

t_node *parse(char **s) {
    t_node *node;
    
    if (**s == '(') {
        (*s)++;
        node = parse(s);
        node = new_node(node, parse(s));
    }
    else if (**s == 'S') node = &t_s;
    else if (**s == 'K') node = &t_k;
    
    (*s)++;
    return node;
}

void print(t_node *node) {
    if(!node)return;
    if(node == &t_s) {
        printf("S");
        return;
    }
    if(node == &t_k) {
        printf("K");
        return;
    }
    printf("(");
    print(node->l);
    print(node->r);
    printf(")");
} 

int isleaf(t_node *node) {
    return (node == &t_s) || (node == &t_k);
}

void freetree(t_node *node) {
    //if (!node || isleaf(node)) return;
    //freetree(node->l);
    //freetree(node->r);
    //free(node);
}

t_node *clonetree(t_node *node){
    //if (!node || isleaf(node)) return node;
    //debug++;
    //return new_node(clonetree(node->l), clonetree(node->r)); 
    return node;
}

t_node *eval(t_node *node) {
    //debug++;
    // Base Case
    if(!node || isleaf(node)) return NULL;
    
    t_node *temp;
    
    // Rule 1
    if(node->l &&
       node->l->l == &t_k) {
               
            temp = node->l->r;
            freetree(node->r);
            free(node->l);
            node = temp;
            return node;
    }
    
    // Rule 2
    if(node->l &&
       node->l->l && 
       node->l->l->l == &t_s) {
              
            temp = node->l->l->r;
            free(node->l->l);
            node->l->l = temp;
            temp = node->l->r;
            node->l->r = node->r;
            
            node->r = new_node(temp, clonetree(node->l->r));
            return node;
    }
    
    // Rule 3
    temp = eval(node->l);
    if(temp) {
            node->l = temp;
            return node;
    }
    
    // Rule 4
    temp = eval(node->r);
    if(temp) {
            node->r = temp;
            return node;
    }

    // Rule 5
    return NULL;
}

int main() {
    
    char buff[1024*64];
    char *c;
    
    t_node *tree = NULL;
    t_node *temp;
    
    freopen("d1_3.in", "rt", stdin);
    freopen("d1_3.out", "wt", stdout);
    
    gets(buff);
    while(strlen(buff)) {
        c = buff;
        tree = parse(&c);   
        while (1) { 
            //print(tree); 
            //printf("\n");
            
            temp = eval(tree);
            if (temp == NULL) break;
            tree = temp; 
        }
        print(tree);
        printf("\n\n%d\n", debug);
        freetree(tree);
        gets(buff);
    }
    return 0;
}

Commented All Additional Frees:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int debug = 0;

typedef struct self_node {
    struct self_node *l;
    struct self_node *r;
} t_node;

t_node t_s = { NULL };
t_node t_k = { NULL };

t_node *new_node(t_node *l, t_node *r) {
    //debug++;
    t_node (* node) = (t_node *)malloc(sizeof(t_node));
    node->l = l;
    node->r = r;
    return node;
}

t_node *parse(char **s) {
    t_node *node;
    
    if (**s == '(') {
        (*s)++;
        node = parse(s);
        node = new_node(node, parse(s));
    }
    else if (**s == 'S') node = &t_s;
    else if (**s == 'K') node = &t_k;
    
    (*s)++;
    return node;
}

void print(t_node *node) {
    if(!node)return;
    if(node == &t_s) {
        printf("S");
        return;
    }
    if(node == &t_k) {
        printf("K");
        return;
    }
    printf("(");
    print(node->l);
    print(node->r);
    printf(")");
} 

int isleaf(t_node *node) {
    return (node == &t_s) || (node == &t_k);
}

void freetree(t_node *node) {
    //if (!node || isleaf(node)) return;
    //freetree(node->l);
    //freetree(node->r);
    //free(node);
}

t_node *clonetree(t_node *node){
    //if (!node || isleaf(node)) return node;
    //debug++;
    //return new_node(clonetree(node->l), clonetree(node->r)); 
    return node;
}

t_node *eval(t_node *node) {
    //debug++;
    // Base Case
    if(!node || isleaf(node)) return NULL;
    
    t_node *temp;
    
    // Rule 1
    if(node->l &&
       node->l->l == &t_k) {
               
            temp = node->l->r;
            freetree(node->r);
            //free(node->l);
            node = temp;
            return node;
    }
    
    // Rule 2
    if(node->l &&
       node->l->l && 
       node->l->l->l == &t_s) {
              
            temp = node->l->l->r;
            //free(node->l->l);
            node->l->l = temp;
            temp = node->l->r;
            node->l->r = node->r;
            
            node->r = new_node(temp, clonetree(node->l->r));
            return node;
    }
    
    // Rule 3
    temp = eval(node->l);
    if(temp) {
            node->l = temp;
            return node;
    }
    
    // Rule 4
    temp = eval(node->r);
    if(temp) {
            node->r = temp;
            return node;
    }

    // Rule 5
    return NULL;
}

int main() {
    
    char buff[1024*64];
    char *c;
    
    t_node *tree = NULL;
    t_node *temp;
    
    freopen("d1_3.in", "rt", stdin);
    freopen("d1_3.out", "wt", stdout);
    
    gets(buff);
    while(strlen(buff)) {
        c = buff;
        tree = parse(&c);   
        while (1) { 
            //print(tree); 
            //printf("\n");
            
            temp = eval(tree);
            if (temp == NULL) break;
            tree = temp; 
        }
        print(tree);
        printf("\n\n%d\n", debug);
        freetree(tree);
        gets(buff);
    }
    return 0;
}

Always Make A New Node Instead Of Modifying Any Subtrees (Just Like The Very Fast Method):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int debug = 0;

typedef struct self_node {
    struct self_node *l;
    struct self_node *r;
} t_node;

t_node t_s = { NULL };
t_node t_k = { NULL };

t_node *new_node(t_node *l, t_node *r) {
    //debug++;
    t_node (* node) = (t_node *)malloc(sizeof(t_node));
    node->l = l;
    node->r = r;
    return node;
}

t_node *parse(char **s) {
    t_node *node;
    
    if (**s == '(') {
        (*s)++;
        node = parse(s);
        node = new_node(node, parse(s));
    }
    else if (**s == 'S') node = &t_s;
    else if (**s == 'K') node = &t_k;
    
    (*s)++;
    return node;
}

void print(t_node *node) {
    if(!node)return;
    if(node == &t_s) {
        printf("S");
        return;
    }
    if(node == &t_k) {
        printf("K");
        return;
    }
    printf("(");
    print(node->l);
    print(node->r);
    printf(")");
} 

int isleaf(t_node *node) {
    return (node == &t_s) || (node == &t_k);
}

void freetree(t_node *node) {
    if (!node || isleaf(node)) return;
    freetree(node->l);
    freetree(node->r);
    free(node);
}

t_node *clonetree(t_node *node){
    if (!node || isleaf(node)) return node;
    return new_node(clonetree(node->l), clonetree(node->r)); 
}

t_node *eval(t_node *node) {
    //debug++;
    // Base Case
    if(!node || isleaf(node)) return NULL;
    
    t_node *temp;
    
    // Rule 1
    if(node->l &&
       node->l->l == &t_k) {
               
            return node->l->r;
    }
    
    // Rule 2
    if(node->l &&
       node->l->l && 
       node->l->l->l == &t_s) {
             
            return new_node(new_node(node->l->l->r, node->r), new_node(node->l->r, node->r));
    }
    
    // Rule 3
    temp = eval(node->l);
    if(temp) {

            return new_node(temp, node->r);
    }
    
    // Rule 4
    temp = eval(node->r);
    if(temp) {

            return new_node(node->l, temp);
    }

    // Rule 5
    return NULL;
}

int main() {
    
    char buff[1024*64];
    char *c;
    
    t_node *tree = NULL;
    t_node *temp;
    
    freopen("d1_3.in", "rt", stdin);
    freopen("d1_3.out", "wt", stdout);
    
    gets(buff);
    while(strlen(buff)) {
        c = buff;
        tree = parse(&c);   
        while (1) { 
            //print(tree); 
            //printf("\n");
            
            temp = eval(tree);
            if (temp == NULL) break;
            tree = temp; 
        }
        print(tree);
        //printf("\n\n%d\n", debug);
        freetree(tree);
        gets(buff);
    }
    return 0;
}

Last edited by Sane; Feb 7th, 2008 at 5:14 PM.
Sane is offline   Reply With Quote
Old Feb 7th, 2008, 5:33 PM   #5
Sane
Banned
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 2,101
Rep Power: 6 Sane will become famous soon enough
Send a message via MSN to Sane
Re: S&K Trees, Differences In Code And Speed

Damn the stupid time limit on the editing! No one's even read my last post, and it's exactly 30 minutes since I posted it. I should be able to simply edit my posts.

So it turns out the last method does work. It just leaks so much memory that it fails around the 6000th iteration or so. I understand now what you meant about the subtrees not being modified. We are allowed to reference the same subtree, in rule 2, instead of cloning it, if we never modify any subtrees. Since now any time we reach a node, we create new nodes. This, in a sense, clones the subtree.

Now I just need to understand incref and decref. I think I get it: Does rc keep track of how many parents a node has? If it ever has zero parents, we can erase it. And if we assign the node to another parent, rc increases? If that's not the case, I'm a lost fool. That seems close, but not quite right.

I will try my idea, and hopefully it will end up matching his code. Let's see how this goes.

Last edited by Sane; Feb 7th, 2008 at 5:47 PM.
Sane is offline   Reply With Quote
Old Feb 7th, 2008, 7:39 PM   #6
The Dark
Expert Programmer
 
Join Date: Jun 2005
Posts: 894
Rep Power: 4 The Dark is on a distinguished road
Re: S&K Trees, Differences In Code And Speed

Thats right - in this case it is how many parents the node has, in the more general case, it is how many times the node is referenced. When the count falls down to zero, then nothing is referencing the node any more, so it can be freed.

Yes, the rules 3 and 4 changing the node would have mucked up the multiple referencer mechanism.

Note: I can't get either of the original two programs to finish with the example SK string. I have been running the reference counting one now for 13 minutes and it is still going. Could you check that the string is the one you are using?
The Dark is offline   Reply With Quote
Old Feb 7th, 2008, 7:51 PM   #7
Sane
Banned
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 2,101
Rep Power: 6 Sane will become famous soon enough
Send a message via MSN to Sane
Re: S&K Trees, Differences In Code And Speed

You need to append a couple trailing newlines to the end of the data file. It only stops when it finds a blank line (part of the input specifications).

Edit: Do you have any reading material, or information I can look up online, that provides a detailed look at reference counting in trees? I can't find any good resources. I'd like a more general overview, including where it helps, when to use it, and why it works.

With the small change, my code now runs in a few milleseconds, but I don't quite understand it all. For instance, I have to reference count the root node when I parse it. Why?

I feel like there's something I don't quite get, and diagrams of the tree and reference counts, in a textbook format, might help immensely. Thanks for all your help too. Truly appreciate it.

Last edited by Sane; Feb 7th, 2008 at 8:09 PM.
Sane is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
I need speed increase FireflyX JavaScript and Client-Side Browser Scripting 5 Dec 22nd, 2007 10:55 AM
Pacman in Python Code acwbrat Python 3 Nov 18th, 2007 1:01 PM
Some Nifty Code Sane Python 2 May 10th, 2005 6:56 AM




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 7:13 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC