Showing posts with label 100 days of code. Show all posts
Showing posts with label 100 days of code. Show all posts

Monday, July 11, 2022

Essay 1: Introduction

Essay 1: Intro to 100 Days of Code

A journey of 10,000 steps begin with the first one


One fine day, warm and sunny, I decided to take up the 100 Days of Code challenge. This was shortly after I joined Twitter and found out that there are all kinds of groups over there. Elon Musk was in the process of buying Twitter and so I decided to join. Neil Gaiman is active there, too. Well, one thing led to another and I was looking over the hashtags and found one that says "100DaysOfCode" and was sufficiently intriqued.


So, I looked over the tweets, and found out that most of them does not feature any source code. And I may have missed it, but the point of doing 100 Days of Code challenge is to improve yourself, and part of that challenge is to publish the source code and share it with the world. The few that did take screenshot of their code do so with their favorite IDE, which inevitably means tiny, tiny font. Extremely hard to read. May as well not bother.


The suggestion was to publish your code on Github, and there's nothing wrong with that suggestion. I do have a blog account that I still post from time to time, and so I decided to just post it there. As long as it's available, no problem!


Most people actually have some kind of planning, either a book, or maybe, an on-line course. The name Angela Yu is featured with many of the posts. Another favorite of people is LeetCode. As for me, I don't have any established plan whatsoever. So, most of my time is spent trying to find the next feasible projects. The constant worry is running out of ideas to try.


The point of this challenge is to learn, and I decided to learn. That means taking something out that I'm uncomfortable with and improve upon it. As of this writing, 3 weeks into the challenge, it's been mixed projects so far. Leetcode challenges are there, but I actually skip most of them because I want to write actual, usable programs. That's a tall order, as I found out that just implementing the command line parameters alone would take upwards half an hour or so. That's just setting variables, no actual program coding.


As to the actual coding, I firmly believe that most of the program's core are actually very simple. To that end, I would screen capture the code from my blog, which means no tiny font! So far, I've been managing to do so in one screen. Exception is the SVG library, which is rather extensive, even in the first incarnation.


The best part of taking this challenge, however, is that it anchors my day. Excepting rest days, which is Sunday, I always code. If the time is lacking, then I code something simple, such as FizzBuzz. Overall, though, it has been extremely productive sessions. 


And that's the real benefit of the process. My coding skill is getting better every day. Of course, it wasn't lacking to begin with, but doing this challenge forces me to be extremely productive with my time. So far, I've been spending about 2-4 hours per day, including write ups. I'm happy with the progress I'm making, especially since other people would code a challenge, and I would code a whole program.


That cannot be overstated: I wrote a whole new program everyday! Not a little function, but a whole program! Now, the program maybe rough and in need of revising, which I will do as needed later. Also, there may be bugs or missing feature which means I spent some time fixing the bugs, especially if it's a program I'll be using later on the challenge. It's still a program a day, though.


That just goes to show, no matter how skillful you are, there will always be higher mountain to climb, and I have begun making my steps, one day at a time. Today, it's the 18th day, and I'm resting and writing this stream of conciousness for the time to reflect back from the day of completion.


2022 July 03


Sunday, July 10, 2022

Day 24/100 Turtle3

Day 24/100 Turtle3

Look! An animated Turtle!


This is just a simple improvement for the Turtle graphic program. I had planned on having multi-turtle going, but the truth is, I also wanted to have some kind of font system going. Unfortunately, I'm really tired at the end of the week, and so decided to just implement one easy, yet eye catching feature: Animation.


SVG standard actually does have animation option, but I'm still fuzzy on the details, so for this one, I decided on animating it the hard way: animated GIF.


Fortunately, it's quite easy to do. All I have to do is to generate a series of frames and then use Image Magick convert program to generate the animated GIF. It will also resize the images automatically. This is the command that I use:


time convert -delay 20 -loop 0 Frame*.svg -resize 320x200 FrameAnim.gif


It takes about 30 seconds to do 23 SVGs animation, so not too speedy. However, for small animation picture, that is acceptable. I do wonder why it takes somewhat long to build a 64kB image.


As to the actual code, it is extremely simple. I have already done a simple SVG generator, and all I did was generate SVGs one after the other. There is a line separator marked with desired filename. This is the code that is used to cause the Turtle to write the separator:


  if (!strncmp("WF",s1,2)) {
    printf("WRITE FILE %s\n",s2);
  }


And that's it. If you remember the Day 16/100 SplitCat program, that is the one being used to split the file into separate ones. Quite a convenience. All it takes is a one liner "WF" to write the files. Okay, I also have to set a string for the filename, and incrementing the counter. I considered having it done internal to the Turtle, but in the end decided against it for transparency considerations. Here's the code:


  for (i=40;i<=90;i+=10) {
  for (j=60;j<=120;j+=20) {
    sprintf(FN,"Frame%02d.svg",c++);
    Turtle("WF",FN,0,0);
    Turtle("SC","",0,0);
    DrawCMYK(i,j);
    Turtle("sc","",0,0);
  }}


So, one line to set the string, and another to write the separator. Two lines total. And that is it for this week. Just a simple Turtle enhancement that will hopefully pay dividends down the road.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

#define MAXENTRY 10000
#define MAXSTR 2560
#define sx0 0
#define sx1 118
#define sy0 0
#define sy1 180
#define PI 3.141528

char Liner[MAXSTR];
char Data[MAXENTRY][MAXSTR];
int numentry;
int Debug=0;

//turtle
char co; //command
int  nu; //Number
int  he; //heading
int  di; //distance
float  an; //angle (rad)
float  tx0,ty0,tx1,ty1; //line
int  pe; //pen color
int  bg; //background color
int mode=0; //0-normal 1-fill
char FontFam[MAXSTR];
int FontSiz;

float map(float x0,float x1, float x2,float y0, float y2) {
  return y0+((x1-x0)*(y2-y0)/(x2-x0)); //y1
}

void SVGHeader() {
  puts("<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\" 
style=\"fill-opacity:1; color-rendering:auto; 
color-interpolation:auto; text-rendering:auto; 
stroke:black; stroke-linecap:square; 
stroke-miterlimit:10; shape-rendering:auto; 
stroke-opacity:0.4; fill:black; 
stroke-dasharray:none; font-weight:normal; 
stroke-width:0.25; font-family:'Dialog'; font-style:normal; 
stroke-linejoin:miter; font-size:12px; 
stroke-dashoffset:0; image-rendering:auto;\" 
width=\"128.0mm\" height=\"190.0mm\" 
viewBox=\"0 0 128.0 190.0\" 
xmlns=\"http://www.w3.org/2000/svg\">
<!--Template generated by the Batik Graphics2D SVG Generator-->
<defs id=\"genericDefs\" />
<g>
<line x1=\"10.0\" y1=\"10.0\" x2=\"118.0\" y2=\"10.0\" />
<line x1=\"118.0\" y1=\"10.0\" x2=\"118.0\" y2=\"180.0\" />
<line x1=\"10.0\" y1=\"180.0\" x2=\"118.0\" y2=\"180.0\" />
<line x1=\"10.0\" y1=\"180.0\" x2=\"10.0\" y2=\"10.0\" />
");
}

void SVGFooter() {
puts("
</g>
</svg>
");
}

void SVGDrawLine(float x0,float y0,float x1,float y1) {
  int t;
  if (mode==1) {
    if (pe) {
      printf("L %f,%f ",x1,y1);
    } else {
      printf("M %f,%f ",x1,y1);
    }
  } else if (pe) {
    printf("<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" />\n",
           x0,y0,x1,y1);
  }
}


int Turtle(char s1[], char s2[], int n1, int n2) {
  if (!strncmp("SC",s1,2)) { //Capture SVG
    SVGHeader();
    if (Debug) puts("SVGStart");
  }
  if (!strncmp("sc",s1,2)) { //Close SVG
    SVGFooter();
    if (Debug) puts("SVGEnd");
  }
  if (!strncmp("F",s1,1)) { //forward absolute
    di=n1;
    if (Debug) printf("Forward Absolute %d\n",di);
    an=map(0.0,(float)he,360.0,0,2*PI);
    tx1=tx0+(float)di*sin(an);
    ty1=ty0-(float)di*cos(an);
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("f",s1,1)) { //forward relative
    di+=n1;
    if (Debug) printf("Forward Relative %d\n",di);
    an=map(0.0,(float)he,360.0,0,2*PI);
    tx1=tx0+(float)di*sin(an);
    ty1=ty0-(float)di*cos(an);
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("G",s1,1)) { //Goto XY Absolute
    tx1=n1; ty1=n2;
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("g",s1,1)) { //Goto XY Relative
    tx1=tx0+n1; ty1=ty0+n2;
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("H",s1,1)) { //Heading Absolute
    he=n1;
    if (Debug) printf("Heading Relative %d\n",he);
  }
  if (!strncmp("h",s1,1)) { //Heading Relative
    he+=n1;
    if (Debug) printf("Heading Relative %d\n",he);
  }
  if (!strncmp("C",s1,1)) { //Color Stroke
    pe=n1;
    if (Debug) printf("Pen color %d\n",pe);
  }
  if (!strncmp("c",s1,1)) { //Color Fill
    bg=n1;
    if (Debug) printf("Background color %d\n",pe);
  }
  if (!strncmp("T",s1,1)) { //Font Fam+Siz
    strcpy(FontFam,s2);
    FontSiz=n1;
  }
  if (!strncmp("t",s1,1)) { //Draw text
    printf("<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d%%\" >"
            ,n1,n2,FontFam,FontSiz);
    printf("%s",s2);
    printf("</text>\n");
  }
  if (!strncmp("M1",s1,2)) { //Mode 1 start
    mode=1;
    if (bg) {
      printf("<path stroke=\"#%6X\" fill-rule=\"evenodd\" fill=\"#%6X\" d=\"\n"
              ,pe,bg);
    } else {
      printf("<path stroke=\"#%6X\" fill-rule=\"evenodd\" fill=\"none\" d=\"\n"
              ,pe,bg);
    }
    printf("M %f,%f ",tx0,ty0);
  }
  if (!strncmp("m1",s1,2)) { //Mode 1 end
    mode=0;
    puts("\" />");
  }
  if (!strncmp("WF",s1,2)) {
    printf("WRITE FILE %s\n",s2);
  }
  return 0;
}

int Init() {
  co=' ';
  nu=0; //Number
  he=0; //heading
  di=0; //distance
  an=0; //angle (deg)
  tx0=59;ty0=90; //line from
  tx1=tx0;ty1=ty0; //line to
  pe=1; //pen
  strcpy(FontFam,"PibotoThin");
  FontSiz=10;
  return 0;
}

void DrawCMYK(int x, int y) {
  Turtle("C","",0x000000,0);
  Turtle("G","",x,y);

  Turtle("C","",0x010101,0);
  Turtle("c","",0x88FFFF,0); //cyan
  Turtle("M1","",0,0);
  Turtle("G","",x,10);
  Turtle("G","",10,10);
  Turtle("G","",10,y);
  Turtle("G","",x,y);
  Turtle("m1","",0,0);
  Turtle("C","",0x010101,0);
  Turtle("c","",0xFF88FF,0); //magenta
  Turtle("M1","",0,0);
  Turtle("G","",x,10);
  Turtle("G","",118,10);
  Turtle("G","",118,y);
  Turtle("G","",x,y);
  Turtle("m1","",0,0);
  Turtle("C","",0x010101,0);
  Turtle("c","",0xFFFF88,0); //yellow
  Turtle("M1","",0,0);
  Turtle("G","",x,180);
  Turtle("G","",10,180);
  Turtle("G","",10,y);
  Turtle("G","",x,y);
  Turtle("m1","",0,0);
  Turtle("C","",0x010101,0);
  Turtle("c","",0x888888,0); //black
  Turtle("M1","",0,0);
  Turtle("G","",x,180);
  Turtle("G","",118,180);
  Turtle("G","",118,y);
  Turtle("G","",x,y);
  Turtle("m1","",0,0);
}

int ProcessData() {
  char Command[MAXSTR];
  char Value[MAXSTR];
  int  Num1=0;
  int  Num2=0;
  int i,j;
  int c=0;
  char FN[MAXSTR];

  for (i=40;i<=90;i+=10) {
  for (j=60;j<=120;j+=20) {
    sprintf(FN,"Frame%02d.svg",c++);
    Turtle("WF",FN,0,0);
    Turtle("SC","",0,0);
    DrawCMYK(i,j);
    Turtle("sc","",0,0);
  }}
  return 0;
}

int main (int argc, char *argv[] ) {
  int n,i;
  Init();
  ProcessData();
  return 0;
}


One last parting thought: I will do something drastic next week. I have been delaying it for some time, and truthfully, I can stay in the same format for some more weeks, but I think it's time for me to incorporate graphical user interface. Since I'm using Raspberry Pi, it's not the easiest thing to do. So, we'll see.


Saturday, July 9, 2022

Day 23/100 Steganography

Day 23/100 Steganography

How do you say Steganosaurus?


I've been working on this source code packager on and off. The whole idea for this program is that most forums on the web restrict the filesize for text, while allowing generous allowance for pictures. So, the idea of including text inside the picture was born.


There are several different ways to do this. You can simply draw the text in the picture, which is what I have been doing. You can encode the text as barcodes or QR codes. Or you can encode the text as black and white dots.


However, those solution involves creating pictures in such an ugly way. So, what if you can just include the text inside the picture itself? Hidden text, so to speak. Hence steganography.


In my case, I decided that since I'm using Raspberry Pi, the display can only show 16 bit color, whereas pictures are usually 24 bit color. So, it is obvious that I should design it as one byte per pixel. The problem is that I cannot just append a byte on the picture. I have to split it as 3-2-3 bits to fit into the 5-6-5 rgb scheme.


It took some doing. I made it easy for myself by skipping PNG format and go for PPM (P6) format. You're going to have to convert it to PNG format, but I'll leave that up to you. Personally, I use ImageMagick convert program.


This is to Write to the image:


      bit=(char) fgetc(fpImg);
      bit&=0xF8; bit|=(c&0xE0)>>5; bit1=bit;
      bit=(char) fgetc(fpImg);
      bit&=0xFC; bit|=(c&0x18)>>3; bit2=bit;
      bit=(char) fgetc(fpImg);
      bit&=0xF8; bit|=(c&0x07); bit3=bit;
      printf("%c%c%c",bit1,bit2,bit3);


And this is to Read from the image:


  for (i=0;i<l;i++) {
      bit1=(char) fgetc(fpImg);
      bit2=(char) fgetc(fpImg);
      bit3=(char) fgetc(fpImg);
      sout= ((bit1 & 0x07)<<5)
                  | ((bit2 & 0x03)<<3)
                  | ((bit3 & 0x07));
      putchar(sout);
  }


If you're wondering why the sout character isn't set to zero before reading in the data, that's because it's not necessary. sout is a character of 8 bits. I'm reading in 8 bits. So, sout variable is going to be wholly overwritten, anyway.


I was able to use the same program for both Write and Read. Depending whether you supply both picture name and data filename, or just the picture name, the program will select the operation appropriately.


 if (argc<2) {
    puts("Write: fileglob [picname.ppm] [datafilename]");
    puts("Read:  fileglob [picname.ppm]");
    puts("ppm is P6 file (binary)");
    return 1;
  }


The hardest part is design, and trying to decipher PPM format. I still have trouble, and I'm not at all sure that I have it. The specification stated that there should be a whitespace (usually newline) after the colordepth, but it gave me an off-by-one error which shifted the color of the picture. I don't think I quiet get it, yet. So, I can only guarantee that the program works on my machine.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXSTR 256
FILE *fpImg;
FILE *fpDat;
char buff[MAXSTR];
int w,h,d;
long l;
char sname[MAXSTR];

int Init(int argc, char *argv[]) {

  if ((fpImg=fopen(argv[1],"r"))==NULL) {
    puts("File open Error");
    return 1;
  }

  if ((fpDat=fopen(argv[2],"r"))==NULL) {
    puts("File open Error");
    return 1;
  }

// Read P6 PPM format
  if (fgets(buff,MAXSTR,fpImg)==NULL) {
    puts("Image file read error");
    return 1;
  }
  if (strncmp("P6",buff,2)) {
    puts("Image file not P6 PPM error");
    return 1;
  }
  puts("P6");

  fseek(fpDat,0L, SEEK_END);
  l = ftell(fpDat);
  fseek(fpDat,0L, SEEK_SET);
  printf("#steg Aa A %ld %s\n",l,argv[2]);


// Read width height
  fscanf(fpImg,"%d %d",&w,&h);  //Line 50
  printf("%d %d\n",w,h);

// Read depth
  fscanf(fpImg,"%s",buff);
  d=atoi(buff);
  printf("%d",d);

  if (d!=255) {
    puts("Image depth isn't 255");
    return 1;
  }

  if (l>(w*h)) {
    puts("Image size too small!");
    printf("Image: %ld  Data: %ld\n",(long)(w*h),l);
    return 1;
  }

  return 0;
}

void CleanUp() {
  if (fpImg!=stdin) fclose(fpImg);
  if (fpDat!=stdin) fclose(fpDat);
}

int ReadData(int argc, char *argv[]) {
  char bit1,bit2,bit3;
  char sout;

  long i;
  long c; char c1;
  char s1[MAXSTR];
  char s2[MAXSTR];
  char s3[MAXSTR];
  char s4[MAXSTR];
  char s5[MAXSTR];

  if ((fpImg=fopen(argv[1],"r"))==NULL) {
    puts("File open Error");
    return 1;
  }

// Read P6 PPM format
  if (fgets(buff,MAXSTR,fpImg)==NULL) {
    puts("Image file read error");
    return 1;
  }
  if (strncmp("P6",buff,2)) {
    puts("Image file not P6 PPM error");
    return 1;
  }
  fscanf(fpImg,"%s %s %s %s %s",s1,s2,s3,s4,s5);
  l=atol(s4); strcpy(sname,s5);

// Read width height
  fscanf(fpImg,"%d %d",&w,&h);  //Line 50

// Read depth
  fscanf(fpImg,"%d",&d);
  if (d!=255) {
    puts("Image depth isn't 255");
    return 1;
  }

  for (i=0;i<l;i++) {
      bit1=(char) fgetc(fpImg);
      bit2=(char) fgetc(fpImg);
      bit3=(char) fgetc(fpImg);
      sout= ((bit1 & 0x07)<<5)
                  | ((bit2 & 0x03)<<3)
                  | ((bit3 & 0x07));
      putchar(sout);
  }

  return 0;
}

void ProcessData() {
  long i,j;
  int c; char bit;
  char bit1,bit2,bit3;
  char sout;

  for (i=0;i<(w*h);i++) {
    c=fgetc(fpDat);
    if (c==EOF) {
      putchar(fgetc(fpImg));
      putchar(fgetc(fpImg));
      putchar(fgetc(fpImg));
    } else {
      bit=(char) fgetc(fpImg);
      bit&=0xF8; bit|=(c&0xE0)>>5; bit1=bit;
      bit=(char) fgetc(fpImg);
      bit&=0xFC; bit|=(c&0x18)>>3; bit2=bit;
      bit=(char) fgetc(fpImg);
      bit&=0xF8; bit|=(c&0x07); bit3=bit;
      printf("%c%c%c",bit1,bit2,bit3);
    }
  }
}

int main (int argc, char *argv[] ) {
  int e=0;

  if (argc<2) {
    puts("Write: fileglob [picname.ppm] [datafilename]");
    puts("Read:  fileglob [picname.ppm]");
    puts("ppm is P6 file (binary)");
    return 1;
  }

  if (argc==2) ReadData(argc,argv);

  if (argc==3) {
    Init(argc,argv);
    ProcessData();
    CleanUp();
  }

  return e;
}


One more thing:

Design is much, much harder than coding! In the process of programming this, I changed the design several times. It took me hours to finish this program up to this state, but that's because I keep changing the design. There used to be more parameters required in order to write the image, as well as trying it out directly with PNG format using Processing. But it's not until PPM idea comes into being that the program finally arrived at satisfactory design.

Thursday, July 7, 2022

Day 22/100 Fibonacci and Factorial Trailing Zero

Day 22/100 Fibonacci and Factorial Trailing Zero

Failure at Grade School Arithmetic


So, I was at conversation the other day, where the other person was touting the virtues of Python, and dissing C language. You know, the language I'm using to do 100 Days of Code challenge. Truthfully, I did encounter a few "Segmentation Fault" core dump error, but I simply took it in stride. What I didn't know, I soon find out. It's part of the learning process.


Why would anybody do it differently? Why would you blame the programming language when you're supposed to increase your own skill so that you no longer make mistakes, instead of expecting the computer to catch yours?


If you look at my programs, you'll see my tendency to be light on error checking routines. What can I say? I don't make that many mistakes. Sure I made some. Everybody does. The difference between me and other people is that I learn from my mistakes and not repeat them. I don't see that kind of commitment from your average coder. This is why I wonder if the oft claimed "Coding makes you smarter" is incorrectly attributed to survivor bias.


Pardon me for having a dim view of the situation. I have seen too many stupid people who don't know what they're doing, make bold claims that are obviously false. Let's not spread more misinformation than what is already out there.


The title above is being honest. The failure does not lie in coding, regardless of platform or programming language chosen. The failure is at grade school level of arithmetic. I'm talking about 5th grader arithmetic here! Add, subtract, multiply, and divide. It doesn't even involve any fraction! How hard can that be?


Very hard, it turns out. Let's take a look at fibonacci code I've written. I did it twice: First is my take on it. The second is what is commonly done by the community, assuming neither Dynamic Programming (memoization) nor Recursion is involved. This is LeetCode 509:


long long fib1(int n) {
  int i=1;
  long long m[2];
  if (n<1)  return 0;
  if (n>92) return -1; //oob
  for (m[0]=1,m[1]=1;--n;i=1-i) m[i]+=m[1-i];
  return m[i];
}

long long fib2(int n) {
  int i=0; long long a=0;
  long long b=1;long long c=1;
  if (n<1)  return 0;
  if (n>92) return -1; //oob
  for (a=0,b=1,c=1;--n;) {
    a=b; b=c; c=a+b;
  }
  return b;
}


As you can see, my version is more compact than what is usually done. It uses only 2 variables, stored as array, so that I can flip between them. Testing does take a while, but if you pride yourself as a software engineer, then you would want to do it that way! Although mathematicians can resort to recursive solution, software engineer cannot do that! For you to take pride in your coding skill as software engineer, the code has to be extremely tight, and no waste either running time (recursion) or memory (memoization). 


The second solution is actually acceptable. It's very commonly done by people who aren't mathematically sophisticated. It's kind of like FizzBuzz challenge. Sure, it's easily done, but is it optimum? Not really. So, the second solution is acceptable, but nothing to be proud of.


As far as recursion and memoization? In my very strong opinion, those are failures! Waste of cycles and bytes! Very unprofessional! I always say that my code is at amateur hobbyist level, but looking at some of these professional coding, I wonder if I'm not already better than most of them.


A second example is even more telling than the fibonacci problem. The problem is to count the number of trailing zeros in factorial. LeetCode 172. Now, we're talking deep into mathematical realm. Sure, leet coder act like it's an easy thing to figure out. But is it? If you can figure out the deep mathematical issues, then surely you can also figure out the most efficient way to compute it? 


Nope! That's a fail! As far as I'm concerned, this kind of question does not belong in the interview process. Fundamentally, either you ran into the problem already, and remember the solution, or alternatively, you haven't run into it previously, and will now have to compute large samples of factorials to try to determine some kind of patterns that you can recognize. Oh, do you know that in these kind of interviews you cannot use any kind of help including computers and browsers? That's right! You only have the white board to do it.


What that tells me is that the company who does these kind of things will fill up their ranks with people who memorize LeetCode problems, instead of the smart coder who can readily research the issue. What's that Einstein, Feynman, et al said? "Do not memorize things that can be looked up?" Exactly!


Let's see what the typical answer is involved. You can probably google this in just a few minutes:


int trail1(int n) {
  int c; int i;
  for (c=0,i=5;n/i>=1;i*=5) {
    c+=(n/i);
  }
  return c;
}


Looks nice and easy, doesn't it? But it's deceptively tricky. Can you even work out the problem even after looking at the code? I certainly cannot! I'm no mathematician. I need to see rows of factorials and count the zeros to get some kind of pattern recognition going. This question is not an easy question, despite the brevity of the solution.


So, am I a failure? Maybe as a mathematician. However, as a coder? Not only would I pass, those people who give the answer above are all failures! Remember, we're evaluating coding skills, here, not math skills. The only math skill you need is grade school level arithmetic! That's right! No more than arithmetic. Hence the failure is of at grade school level. Now tell me, what kind of highly skilled, highly experienced, highly paid, professional computer programmer would fail grade school math? Only impostors do.


You need to have proper foundation in your skills! This goes double if you want to be a professional! No short cuts! If you cannot do grade school arithmetic, I suggest taking up coding as a hobby, rather than being a professional.


Here is the proper solution to the question:


int trail2(int n) {
  int c=0;
  for (;n/=5;c+=n);
  return c;
}


One division, and one addition. That's it! 


Look, coding is hard. I understand that. What I don't understand is why would people fail at grade school arithmetic! The usual solution as shown above features 2 divisions and a multiply. How is that better? It's not better. It's not professional at all!


How many years ago did you first learn arithmetic? Are you still at that level? Sigh.


Sorry to be so negative about things, but I'm seeing way more incompetence going on lately than what it used to be. Unfortunately, it seems that people just don't care anymore. That's a dangerous attitude to have. In the worst case scenario, it will mean the whole destruction of the industry as we know it. Don't laugh. It happened before. Look up "Dot Com Meltdown" if you don't believe me. That's the equivalent of "Game Industry crash" or even "Great Depression" level of apocalyptic event. Let's not have another one, okay?


Sigh.


I keep telling people to learn fundamentals, but people just don't bother. They refuse to do so. Why would they? A convenient library is just a download away! Until one day, one of those library disappear and if you google "... brought down the internet", you will find some incredible stories that shouldn't happen, but that they happened!


Study your fundamentals!


Lookit dem Smileys...


int d8(int B) {
  int              O;
  return  (O=  (B  /=  5)
  )?  O+   d8  (B  ):  0;
}


One more thing: Python isn't English! That distinction belongs to Literary Programming as pioneered by Dr. Donald Knuth. Alternatively, you can pull up just about any Design Document. Are you a Rockstar computer programmer?


Wednesday, July 6, 2022

Day 21/100 Roman Numeral

Day 21/100 Roman Numeral

MMXX has been a bad year!


I'll just do a quick run today. Basically, I'm intrigued by the possibility of parsing text with text fragments, instead per character or per words. So, I decided to put out an array of text and the converted value. Since this is a quick experiment, and I want to have an actual program, I decided to do Roman numeral conversion. Leet Code 13


I don't have to do Roman numeral conversion program. I can do Morse Code, or Phonetic Alphabet, etc. But Roman numeral conversion is simple enough, so why not?


int roman(char *num) {
  int i; int n=0; int l;
  for (i=0;i<strlen(num);i++) num[i]=toupper(num[i]);
  while (strlen(num)) {
    for (i=0;i<MAXENTRY;i++) {
      l=strlen(Romc[i]);
      if (!strncmp(Romc[i],num,l)) {
        n+=Romv[i]; num+=l; break;
      }
    }
  }
  return n;
}


It took me about an hour to do this, simply because I forgot to Initialize the table! Oh, dear. Can we say C is a terrible language to work with? Not really. I mean, what kind of computer programmer is it, to forget to initialize the table? Also, what kind of computer language can help with that? Syntax error is easy. Semantic error, however, is something you just have to own it.


#include <stdio.h>
#include <ctype.h>
#include <string.h>

#define MAXDIGIT 5
#define MAXENTRY 13

char Romc[MAXENTRY][MAXDIGIT];
int  Roml[MAXENTRY];
int  Romv[MAXENTRY];

int roman(char *num) {
  int i; int n=0; int l;
  for (i=0;i<strlen(num);i++) num[i]=toupper(num[i]);
  while (strlen(num)) {
    for (i=0;i<MAXENTRY;i++) {
      l=strlen(Romc[i]);
      if (!strncmp(Romc[i],num,l)) {
        n+=Romv[i]; num+=l; break;
      }
    }
  }
  return n;
}

void Init() {
  strcpy(Romc[0],"CM"); Romv[0]=900;
  strcpy(Romc[1],"CD"); Romv[1]=400;
  strcpy(Romc[2],"XC"); Romv[2]=90;
  strcpy(Romc[3],"XL"); Romv[3]=40;
  strcpy(Romc[4],"IX"); Romv[4]=9;
  strcpy(Romc[5],"IV"); Romv[5]=4;
  strcpy(Romc[6],"M");  Romv[6]=1000;
  strcpy(Romc[7],"D");  Romv[7]=500;
  strcpy(Romc[8],"C");  Romv[8]=100;
  strcpy(Romc[9],"L");  Romv[9]=50;
  strcpy(Romc[10],"X"); Romv[10]=10;
  strcpy(Romc[11],"V"); Romv[11]=5;
  strcpy(Romc[12],"I"); Romv[12]=1;
}

int main (int argc, char *argv[] ) {
  int i;

  if   (argc<2) {
    puts("Usage: roman ROMAN\n");
    return 0;
  }
  Init();
  for (i=1;i<argc;i++) {
    printf("%6d %s\n",roman(argv[i]),argv[i]);
  }
  return 0;
}


One more thing: You probably notice that the logic used here isn't the same as the logic used everywhere else. That's because the goal isn't to convert Roman numerals per se, but as an exercise to parse partial text not separated by words. Those of you who depends on pre-existing libraries, do you even have one? In any language? Sometimes, you just have to buckle down and make your own library. There's no way around it if you want to work on the bleeding edge.


Tuesday, July 5, 2022

Day 20/100 Factor

Day 20/100 Factor

Primes and Factors


It's been 20 days so far, and I'm at a point where my momentum has been disrupted. This is because I'm at research stage where things are unknown. This week will probably feature the slowest progress as I am reorganizing my codes. Specifically, I'm doing this to create programs to use. I think that's the difference between my effort and other people's. They learn to code, I'm writing programs to use.


It's possible that I will take a week off next week as I am re-orienting myself. I still code everyday, just that I won't be showing off the programs I've written as they are too messy and raw.


Take today's program: It's Factor. There's actually a built-in program to do this. Have you seen the source code? It's rather messy! I'm certainly no mathematician. So, I did it the simple way. Even though I'm doing it the simplest way, it does the job quite fast most of the time. The internal program runs at 0.005s, while mine is at 0.027s. That's about 5 times difference, and that's quite normal. In other words, to achieve single digit multiple of performance, there's quite a bit you must do! That's fine by my book. Also remember that I'm not using Gnu C Compiler (GCC). I'm using Tiny C Compiler (TCC) because it compiles so fast.


Given all the disadvantages, I'm happy that the program performs at the speed that it is. Of course, if I'm factoring numbers greater than 32 bit, it can bog down quite a bit. Some of the larger numbers, such as those of 64 bit, may even take hours to do. However, the algorithm I use is very simple:


  printf("F1 %lld: ",n);
  for (i=1;i<=n;i++) {
    while ((n%i)==0) {
      printf("%lld ",i);
      n/=i; c++;
      if (i==1) i++;
    }
  }
  printf("\n");
  if (c==2) return 1;
  return 0;



  printf("F2 %lld: 1 ",n);
  for (i=n/2;n>1 && i>1;i--,c++) {
    if ((n%i)==0) {
      printf("%lld ",(n/i));
      n=i;
    }
  }
  printf("%lld\n",n);
  return (n/i);


As you can see, there are 2 versions of code. Both returns a condition if the number is prime, so you can check the number for prime. Like so:


    if (factor1(n)) puts("Prime");
    if (n==factor2(n)) puts("Prime");


And that makes all the difference. Trying to give that is what makes the code so challenging to write, especially since I'm approaching the problem from both direction: Increasing and Decreasing.


Most people, when given a problem, will solve it one way, but never bothered to solve it again another way. That's no way to learn! Always do it more than once. In fact, I habitually do it at least 3 times: The good way, the better way, and the best way. Adopting that philosophy yields the most efficient result in regard to cost/performance gain, and so I would encourage you to do it that way, too.


At the very least, try to approach the problem from opposite point of view. If you look left, look right! If you look up, look down! If you look forward, look backward! It's that easy! There's nothing stopping you from doing it, except yourself. 


The LeetCode challenge 254 expects you to output combinations as well, but when's the last time that kind of algorithm was used? Nothing that I can think of. Anyway, here is the completed factor program:

 
#include <stdio.h>
#include <stdlib.h>

int factor1(long long n) {
  long long i;
  long long c=0;

  printf("F1 %lld: ",n);
  for (i=1;i<=n;i++) {
    while ((n%i)==0) {
      printf("%lld ",i);
      n/=i; c++;
      if (i==1) i++;
    }
  }
  printf("\n");
  if (c==2) return 1;
  return 0;
}


int factor2(long long n) {
  long long i; long long c=0;
  printf("F2 %lld: 1 ",n);
  for (i=n/2;n>1 && i>1;i--,c++) {
    if ((n%i)==0) {
      printf("%lld ",(n/i));
      n=i;
    }
  }
  printf("%lld\n",n);
  return (n/i);
}

int main (int argc, char *argv[]) {
  int i,n;
  if (argc<2) { puts("factor [NUMBER] ..."); return 0; }

  for (i=1;i<argc;i++) {
    n=atoll(argv[i]);
    if (factor1(n)) puts("Prime");
    if (n==factor2(n)) puts("Prime");
  }
  return 0;
}


One more thing: In this case, I did another try another factoring algorithm involving recursion, but that didn't pan out, it being a fragile algorithm, so I didn't bother to show you. But it does take a few hours of my time as I try the algorithm, just so you know.


Monday, July 4, 2022

Day 19/100 FizzBuzz

Day 19/100 FizzBuzz

FizzBuzz Buzzineezz


Yeah, you know it's going to happen eventually. I Fizz the FizzBuzz Buzz. Lame, I know. But here it is...


FizzBuzz program basically prints out numbers from 1 to 100, except that if the number is evenly divisible by 3, it prints "Fizz" instead. If the number is evenly divisible by 5, it prints "Buzz" instead. And if the number is evenly divisible by 3 and 5, it prints "FizzBuzz" instead. This is LeetCode 412.


So, here is the first solution that comes to mind. This isn't a unique solution, by any means, just the one most readily comes to mind.


FB1

void FizzBuzz1() {
  int i;
  for (i=1;i<=100;i++) {
    if (!(i%3) && !(i%5)) printf("FizzBuzz\n");
    else if (!(i%3)) printf("Fizz\n");
    else if (!(i%5)) printf("Buzz\n");
    else printf("%d\n",i);
  }
}



The problem here is that the solution isn't exactly clever. It works, but that's about it. Another thing to worry about is that the program isn't really easily extended. How about if you want to add "Bazz" for numbers evenly divisible by 7? And add "Bang" for numbers evenly divisible by 9? You can see that hard coding the solution like that doesn't bring any prestige point to the programmer. As a side note, failing at the task will certainly bring shame to the programmer.


So, let's improve the solution. Thinking about it, the problem is that it has different states to the solution. Especially important is that you don't want to show the number if the answer is "Fizz", "Buzz", or both! That's a failure point, so be careful!


The standard professional engineering way to solve this is via states. Something like this:


FB2

void FizzBuzz2() {
  int i;
  int s=0;
  for (s=0,i=1;i<=100;s=0,i++) {
    if (!(i%3)) s+=1;
    if (!(i%5)) s+=2;
    switch (s) {
      case 0: printf("%d\n",i); break;
      case 1: printf("Fizz\n"); break;
      case 2: printf("Buzz\n"); break;
      case 3: printf("FizzBuzz\n"); break;
    }
  }
}


And that works. But it's somewhat big, over engineered program, isn't it? In fact, that is the professsional way to do things. I guess now you know why I'm firmly at the side of amateur hobbyist side. I no longer have any patience in overbearing bureaucratic structures. Well, if the program needs it, I don't mind. Usually, they don't need it, though. Let's simplify that monstrosity.


FB3

void FizzBuzz3() {
  int i;
  int s=0;
  for (i=1;i<=100;i++) {
    s=0;
    if (!(i%3)) { s=1; printf("Fizz"); }
    if (!(i%5)) { s=1; printf("Buzz"); }
    if (!s)     {      printf("%d",i); }
    printf("\n");
  }
}


So, in fact, we only need one state. Are we printing number? Or not? We don't need to get fancy! And that's a good solution, too, albeit less structured, and perhaps a little bit more difficult to read. Let's compact the program even more!


FB4

void FizzBuzz4() {
  int i;
  for (i=1;i<=100;i++) {
    printf("%d\r%s%s\n",i,
    (i%3)?"":"Fizz",
    (i%5)?"":"Buzz");
  }
}


It does look like it works and it's only one line, but there's a trick to it. The output depends on the fact that it uses '\r' carriage return character, which means that the number is always displayed, but hidden when FizzBuzz occur. Visually, it's the same thing, but if you check the output via program such as diff, then it will fail. Still, that's a neat hack that you can do to impress people!


FB5

void FizzBuzz5() {
  int i=0;
  while(100>i++){switch(p("%s%s",(i
  %3)?"":"Fizz",(i%5)?"":"Buzz")) {
  case 0:p("%d",i);default:p("\n");
}}}


FB6

void FizzBuzz6() {
  for(int i=0;100>i++;)
  switch(p("%s%s",(i%3)
  ?"": "Fizz",(i%5)?"":
  "Buzz")){case 0:p("%d"
  ,i);default :p("\n");
}}


Then again, there is impressive, and just plain old ridiculous! There's only a fine line between the two.


One more thing: If you're wondering what's p(), it's printf(). I simply define it in the beginning of the program.


Sunday, July 3, 2022

Day 18/100 Turtle2 (Improved)

 Day 18/100 Turtle 2 (Improved)

Do turtles come in red?


If you remember Day 14, I did a simple turtle graphic program. Well, I revisited the program, and add a bunch of features. A little too much, maybe. It has over 300 lines on it. Quite a program. Yet, it is not complete yet. There's still quite a few capabilities I want to put it in, but it suffices for now.


The code is interesting because of the function Turtle() actually acts like an object. Structurally, it has 4 parameters: 2 strings and 2 numbers. The first string is actually a command for the turtle. Object methods, so to speak. The rest are the parameters, except it's not object oriented. This is just plain old C, instead of C++.


If I want to, I can do it as a graphic library, and I still have an option to do that. Just do it as an abstraction layer, or something. But it's getting complicated, and I'm thinking maybe I should stop here for now, and build some kind of documentation. As it is, it's good enough for some fancy graphic:



A picture of a random tree


Most commands comes in pairs. Usually the point is obvious if you look at the code. The function Turtle() is rather large, but if you look at individual commands, it's actually rather simple for each. Something to remember that doing graphic is usually a library call away. Well, not this one. Here, I simply put out SVG commands, and that's a simple text output.


  //TreeMaker with Spark?
  if (r>30) {
  for (i=3+(rand()%n);i;i--) {
    x1=x0+(rand()%r-(r/2));
    y1=y0+(rand()%r-(r/3));
    Turtle("G","",x0,y0);
    Turtle("C","",0x102030,0);
    Turtle("G","",x1,y1);
    Spark1(x1,y1,n/2,r/2);
  }}



  Turtle("SC","",0,0);
  srand(3333);Turtle("C","",0x010101,0);
  Turtle("G","",60,20);Spark1(60,90,32,75);
  srand(6666);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,70);
  srand(9999);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,60);
  srand(18100);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark2(60,90,32,60);
  Turtle("sc","",0,0);


This is the main code for drawing the tree. It uses 2 Spark functions. Originally, I used a recursive function, but then I added color, and well, there you go. I'd say the tree looks good enough for something that is random.


I also added some SVG capabilities, such as Fonts. I'm thinking that maybe I should just change the name from Turtle to SVG Library. Oh, well, that is something to do in the future. I may as well do it, too. It's no longer a turtle graphic program when I eliminate turn left and turn right. Those has been replaced with Heading(relative) and Heading(absolute). 


Furthermore, I changed the program so that it can handle multiple character command. So, with that, it has greatly expanded the capability of taking in numerous commands. BTW, most commands comes in upper case and lower case versions.


SC: prints out SVG file header
sc: prints out SVG file footer
F - n1: Forward, absolute distance
f - n1: forward, relative distance
G - n1,n2: Goto XY, absolute
g - n1,n2: Goto XY, relative
H - n1: Heading, absolute (0-360)
h - n1: Heading, relative
C - n1: Stroke (pen) color. 0 for pen up
c - n1: Fill color. 0 for "none"
T - s2,n1: Set Font, Font name, size
t - s2,n1,n2: Draw Text. Text,X,Y
M1 - EvenOdd Fill mode. Uses background color
m1 - disable fill mode.


These command set is enough to allow for some sophisticated graphic. I was even able to include the SVG code in my blog! So, that's good.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>

#define MAXENTRY 10000
#define MAXSTR 2560
#define sx0 0
#define sx1 118
#define sy0 0
#define sy1 180
#define PI 3.141528


char Liner[MAXSTR];
char Data[MAXENTRY][MAXSTR];
int numentry;
int Debug=0;

//turtle
char co; //command
int  nu; //Number
int  he; //heading
int  di; //distance
float  an; //angle (rad)
float  tx0,ty0,tx1,ty1; //line
int  pe; //pen color
int  bg; //background color
int mode=0; //0-normal 1-fill
char FontFam[MAXSTR];
int FontSiz;

 

float map(float x0,float x1, float x2,float y0, float y2) {

  return y0+((x1-x0)*(y2-y0)/(x2-x0)); //y1

}


void SVGHeader() {
  puts("<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\" 
style=\"fill-opacity:1; color-rendering:auto; 
color-interpolation:auto; text-rendering:auto; 
stroke:black; stroke-linecap:square; 
stroke-miterlimit:10; shape-rendering:auto; 
stroke-opacity:0.4; fill:black; 
stroke-dasharray:none; font-weight:normal; 
stroke-width:0.25; font-family:'Dialog'; font-style:normal; 
stroke-linejoin:miter; font-size:12px; 
stroke-dashoffset:0; image-rendering:auto;\" 

width=\"128.0mm\" height=\"190.0mm\" 
viewBox=\"0 0 128.0 190.0\" 

xmlns=\"http://www.w3.org/2000/svg\">
<!--Template generated by the Batik Graphics2D SVG Generator-->
<defs id=\"genericDefs\" />

<g>
<line x1=\"10.0\" y1=\"10.0\" x2=\"118.0\" y2=\"10.0\" />
<line x1=\"118.0\" y1=\"10.0\" x2=\"118.0\" y2=\"180.0\" />
<line x1=\"10.0\" y1=\"180.0\" x2=\"118.0\" y2=\"180.0\" />
<line x1=\"10.0\" y1=\"180.0\" x2=\"10.0\" y2=\"10.0\" />
");
}


void SVGFooter() {
  puts("
</g>
</svg>
");
}


void SVGDrawLine(float x0,float y0,float x1,float y1) {
  int t;

  if (mode==1) {
    if (pe) {
      printf("L %f,%f ",x1,y1);
    } else {
      printf("M %f,%f ",x1,y1);
    }
  } else if (pe) {
    printf("<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" />\n",
           x0,y0,x1,y1);
  }
}

int Turtle(char s1[], char s2[], int n1, int n2) {

  if (!strncmp("SC",s1,2)) { //Capture SVG
    SVGHeader();
    if (Debug) puts("SVGStart");
  }
  if (!strncmp("sc",s1,2)) { //Close SVG
    SVGFooter();
    if (Debug) puts("SVGEnd");
  }
  if (!strncmp("F",s1,1)) { //forward absolute
    di=n1;
    if (Debug) printf("Forward Absolute %d\n",di);
    an=map(0.0,(float)he,360.0,0,2*PI);
    tx1=tx0+(float)di*sin(an);
    ty1=ty0-(float)di*cos(an);
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("f",s1,1)) { //forward relative
    di+=n1;
    if (Debug) printf("Forward Relative %d\n",di);
    an=map(0.0,(float)he,360.0,0,2*PI);
    tx1=tx0+(float)di*sin(an);
    ty1=ty0-(float)di*cos(an);
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("G",s1,1)) { //Goto XY Absolute
    tx1=n1; ty1=n2;
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("g",s1,1)) { //Goto XY Relative
    tx1=tx0+n1; ty1=ty0+n2;
    SVGDrawLine(tx0,ty0,tx1,ty1);
    tx0=tx1;ty0=ty1;
  }
  if (!strncmp("H",s1,1)) { //Heading Absolute
    he=n1;
    if (Debug) printf("Heading Absolute %d\n",he);
  }
  if (!strncmp("h",s1,1)) { //Heading Relative
    he+=n1;
    if (Debug) printf("Heading Relative %d\n",he);
  }
  if (!strncmp("C",s1,1)) { //Color Stroke
    pe=n1;
    if (Debug) printf("Pen color %d\n",pe);
  }
  if (!strncmp("c",s1,1)) { //Color Fill
    bg=n1;
    if (Debug) printf("Background color %d\n",pe);
  }
  if (!strncmp("T",s1,1)) { //Font Fam+Siz
    strcpy(FontFam,s2);
    FontSiz=n1;
  }
  if (!strncmp("t",s1,1)) { //Draw text
    printf("<text x=\"%d\" y=\"%d\" font-family=\"%s\" font-size=\"%d%%\" >"
            ,n1,n2,FontFam,FontSiz);
    printf("%s",s2);
    printf("</text>\n");
  }
  if (!strncmp("M1",s1,2)) { //Mode 1 start
    mode=1;
    if (bg) {
      printf("<path stroke=\"#%6X\" fill-rule=\"evenodd\" fill=\"#%6X\" d=\"\n"
              ,pe,bg);
    } else {
      printf("<path stroke=\"#%6X\" fill-rule=\"evenodd\" fill=\"none\" d=\"\n"
              ,pe,bg);
    }
    printf("M %f,%f ",tx0,ty0);
  }
  if (!strncmp("m1",s1,2)) { //Mode 1 end
    mode=0;
    puts("\" />");
  }
  return 0;
}


int Init() {
  co=' ';
  nu=0; //Number
  he=0; //heading
  di=0; //distance
  an=0; //angle (deg)
  tx0=59;ty0=90; //line from
  tx1=tx0;ty1=ty0; //line to
  pe=1; //pen
  strcpy(FontFam,"PibotoThin");
  FontSiz=10;
  return 0;
}

int ProcessData() {
  char Command[MAXSTR];
  char Value[MAXSTR];
  int  Num1=0;
  int  Num2=0;
  int i,j;


  Turtle("C","",0x112233,0);
  Turtle("c","",0xCCBBAA,0);
  Turtle("SC","",0,0);
//  Turtle("M1","",0,0);
  for (i=10;i<170;i+=5) {
    Turtle("C","",0,0);
    Turtle("G","",10,i);
    Turtle("C","",0x102030,0);
    Turtle("G","",113,i);
  }
//  Turtle("m1","",0,0);
  Turtle("T","FreeMono",40,0);
  Turtle("t","FreeMono 40",15,20);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,25);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,30);
  Turtle("T","FreeSans",40,0);
  Turtle("t","FreeSans 40",15,35);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,40);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,45);
  Turtle("T","FreeSerif",40,0);
  Turtle("t","FreeSerif 40",15,50);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,55);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,60);

  Turtle("T","Quicksand Light",50,0);
  Turtle("t","Quicksand Light 50",15,70);
  Turtle("t","The quick brown fox jumps over the",15,75);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,80);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,85);

  Turtle("T","Quicksand Light",40,0);
  Turtle("t","Quicksand Light 40",15,95);
  Turtle("t","The quick brown fox jumps over the lazy dog",15,100);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,105);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,110);

  Turtle("T","Quicksand Light",30,0);
  Turtle("t","Quicksand Light 30",15,120);
  Turtle("t","The quick brown fox jumps over the lazy dog",15,125);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,130);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,135);

  Turtle("T","Quicksand Light",20,0);
  Turtle("t","Quicksand Light 20",15,145);
  Turtle("t","The quick brown fox jumps over the lazy dog",15,150);
  Turtle("t","ABCDEFGHIKLMNOPQRSTUVWXYZ",15,155);
  Turtle("t","abcdefghijklmnopqrstuvwxyz",15,160);

  Turtle("sc","",0,0);
  return 0;
}

int Spark1(int x0,int y0, int n, int r) {
  int i;
  int x1,y1;

  if (r>30) {
  for (i=3+(rand()%n);i;i--) {
    x1=x0+(rand()%r-(r/2));
    y1=y0+(rand()%r-(r/3));
    Turtle("G","",x0,y0);
    Turtle("C","",0x102030,0);
    Turtle("G","",x1,y1);
    Spark1(x1,y1,n/2,r/2);
  }}

  if (r<=30) {
    Turtle("c","",0x99FF99,0);
    Turtle("M1","",0,0);
    for (i=3+(rand()%n);i;i--) {
      x1=x0+(rand()%r-(r/4));
      y1=y0+(rand()%r-(r/3));
      Turtle("G","",x1,y1);
    }
    Turtle("m1","",0,0);
  }
  return 0;
}

 

int Spark2(int x0,int y0, int n, int r) {

  int i;

  int x1,y1;


  if (r>15) {
  for (i=3+(rand()%n);i;i--) {
    x1=x0+(rand()%r-(r/2));
    y1=y0+(rand()%r-(r/3));
    Turtle("G","",x0,y0);
    Turtle("C","",0x102030,0);
    Turtle("G","",x1,y1);
    Spark2(x1,y1,n/2,r/2);
  }}

  if (r<=15) {
    Turtle("c","",0x338833,0);
    Turtle("M1","",0,0);
    for (i=3+(rand()%n);i;i--) {
      x1=x0+(rand()%r-(r/4));
      y1=y0+(rand()%r-(r/3));
      Turtle("G","",x1,y1);
    }
    Turtle("m1","",0,0);
  }
  return 0;
}


int main (int argc, char *argv[] ) {
  int n,i;

  Init();
//  ProcessData();
  //TreeMaker with Spark?
  Turtle("SC","",0,0);
  srand(3333);Turtle("C","",0x010101,0);
  Turtle("G","",60,20);Spark1(60,90,32,75);
  srand(6666);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,70);
  srand(9999);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark1(60,90,32,60);
  srand(18100);Turtle("C","",0,0);
  Turtle("G","",60,20);Spark2(60,90,32,60);
  Turtle("sc","",0,0);
  return 0;
}


One more thing: Should I actually just make this an SVG library with Turtle layer?