Related
Here is my test code to find 1st clipping area on the screen.
Two subroutines and dummy loops in the code to compare the performance of them.
point_in_neon (NEON version) and point_in (Regular version) does the same thing:
find out the first clipping area (contains given point) in given list and return -1 if there is no matching area.
I expected NEON version is faster than regular version.
Unfortunately, it is slower than regular version. Is there another way to speed it up?
The compiler command is:
${CC} -O2 -ftree-vectorize -o vcomp vcomp.c
Thanks,
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <sys/time.h>
#include <arm_neon.h>
#define WIDTH (4096)
#define HEIGHT (4096)
#define CLIPS (32)
static inline uint64_t now(void) {
struct timeval tv;
gettimeofday(&tv,NULL);
return tv.tv_sec*1000000+tv.tv_usec;
}
typedef struct _rect_t {
int32_t x;
int32_t y;
uint32_t width;
uint32_t height;
} rect_t;
typedef struct _point_t {
int32_t x;
int32_t y;
} point_t;
int32_t inline point_in_neon(const point_t *pt, const rect_t rs[4]) {
const int32_t right[4]={
rs[0].x+rs[0].width-1,
rs[1].x+rs[1].width-1,
rs[2].x+rs[2].width-1,
rs[3].x+rs[3].width-1
}, bottom[4]={
rs[0].y+rs[0].height-1,
rs[1].y+rs[1].height-1,
rs[2].y+rs[2].height-1,
rs[3].y+rs[3].height-1
};
int32x4_t p, r;
uint32x4_t t;
uint32_t res[4];
//p = <Xp, Xp, Xp, Xp>
p=vld1q_dup_s32(&pt->x);
//r = <Left0, Left1, Left2, Left3>
r=vld1q_lane_s32(&rs[0].x, r, 0);
r=vld1q_lane_s32(&rs[1].x, r, 1);
r=vld1q_lane_s32(&rs[2].x, r, 2);
r=vld1q_lane_s32(&rs[3].x, r, 3);
//t = (p >= r)
t=vcgeq_s32(p, r);
//r = <Right0, Right1, Right2, Right3>
r=vld1q_s32(&right);
//t = t & (r >= p)
t=vandq_u32(t, vcgeq_s32(r, p));
//p = <Yp, Yp, Yp, Yp>
p=vld1q_dup_s32(&pt->y);
//r = <Top0, Top1, Top2, Top3>
r=vld1q_lane_s32(&rs[0].y, r, 0);
r=vld1q_lane_s32(&rs[1].y, r, 1);
r=vld1q_lane_s32(&rs[2].y, r, 2);
r=vld1q_lane_s32(&rs[3].y, r, 3);
//t = t & (p >= r)
t=vandq_u32(t, vcgeq_s32(p, r));
//r = <Bottom0, Bottom1, Bottom2, Bottom3>
r=vld1q_s32(&bottom);
//t = t & (r >= p)
t=vandq_u32(t, vcgeq_s32(r, p));
vst1q_u32(res, t);
if(res[0])
return 0;
else if(res[1])
return 1;
else if(res[2])
return 2;
else if(res[3])
return 3;
return -1;
}
int32_t inline point_in(const point_t *pt, const rect_t *rs, uint32_t len) {
int32_t i;
for(i=0;i<len;i++) {
int32_t right=rs[i].x+rs[i].width-1,
bottom=rs[i].y+rs[i].height-1;
if(pt->x>=rs[i].x && pt->x<=right &&
pt->y>=rs[i].y && pt->y<=bottom)
return i;
}
return -1;
}
int32_t main(int32_t argc, char *argv[]) {
rect_t rs[CLIPS];
int32_t i, j;
uint64_t ts0, ts1;
int32_t res[2][CLIPS];
srand((unsigned int)time(NULL));
for(i=0;i<CLIPS;i++) {
rs[i].x=rand()%WIDTH;
rs[i].y=rand()%HEIGHT;
rs[i].width=rand()%WIDTH;
rs[i].height=rand()%HEIGHT;
}
memset(res, 0, sizeof(res));
ts0=now();
for(i=0;i<HEIGHT;i++) {
for(j=0;j<WIDTH;j++) {
point_t p={i, j};
int32_t idx=point_in(&p, rs, CLIPS);
if(idx>=0)
res[0][idx]=1;
}
}
ts0=now()-ts0;
ts1=now();
for(i=0;i<HEIGHT;i++) {
for(j=0;j<WIDTH;j++) {
int32_t k, idx;
point_t p={i, j};
for(k=0, idx=-1;k<CLIPS/4;k++) {
idx=point_in_neon(&p, &rs[k*4]);
if(idx>=0)
break;
}
if(idx>=0)
res[1][k*4+idx]=1;
}
}
ts1=now()-ts1;
/*
for(i=0;i<CLIPS;i++) {
if(res[0][i]!=res[1][i]) {
printf("error.\n");
return 1;
}
}
*/
printf("regular = %lu\n", ts0);
printf("neon = %lu\n", ts1);
return 0;
}
According to Peter Cordes's suggestion, I replaced data loding parts of point_in_neon subroutine with vld4q_s32 intrinsic and subsequent right and bottom calculation can be vectorized. Now the code is shorter and faster than regular version.
int32_t inline point_in_neon(const point_t *pt, const rect_t rs[4]) {
int32x4x4_t r;
int32x4_t right, bottom, p;
uint32x4_t t;
uint32_t res[4];
/*
r.val[0] = <X0, X1, X2, X3>
r.val[1] = <Y0, Y1, Y2, Y3>
r.val[2] = <Width0, Width1, Width2, Width3>
r.val[3] = <Height0, Height1, Height2, Height3>
*/
r=vld4q_s32(rs);
//right = <Right0, Right1, Right2, Right3>
right=vsubq_s32(vaddq_s32(r.val[0], r.val[2]), vdupq_n_s32(1));
//bottom = <Bottom0, Bottom1, Bottom2, Bottom3>
bottom=vsubq_s32(vaddq_s32(r.val[1], r.val[3]), vdupq_n_s32(1));
//p = <Xp, Xp, Xp, Xp>
p=vld1q_dup_s32(&pt->x);
//t = (p >= left)
t=vcgeq_s32(p, r.val[0]);
//t = t & (right >= p)
t=vandq_u32(t, vcgeq_s32(right, p));
//p = <Yp, Yp, Yp, Yp>
p=vld1q_dup_s32(&pt->y);
//t = t & (p >= top)
t=vandq_u32(t, vcgeq_s32(p, r.val[1]));
//t = t & (r >= bottom)
t=vandq_u32(t, vcgeq_s32(bottom, p));
vst1q_u32(res, t);
if(res[0])
return 0;
else if(res[1])
return 1;
else if(res[2])
return 2;
else if(res[3])
return 3;
return -1;
}
Starting with your original point_in method, we can clean up a little bit here by removing the -1's, and changing <= to <.
int32_t inline point_in(const point_t *pt, const rect_t *rs, uint32_t len) {
int32_t i;
for(i=0; i < len; i++)
{
// this is pointless - change your data structures so that
// the rect stores minx/maxx, miny/maxy instead!
int32_t right = rs[i].x + rs[i].width;
int32_t bottom= rs[i].y + rs[i].height;
bool cmp0 = pt->x >= rs[i].x;
bool cmp1 = pt->y >= rs[i].y;
bool cmp2 = pt->x < right;
bool cmp3 = pt->y < bottom;
if(cmp0 & cmp1 & cmp2 & cmp3)
return i;
}
return -1;
}
Next obvious thing to point out:
// your screen size...
#define WIDTH (4096)
#define HEIGHT (4096)
// yet your structures use uint32 as storage???
typedef struct _rect_t {
int32_t x;
int32_t y;
uint32_t width;
uint32_t height;
} rect_t;
typedef struct _point_t {
int32_t x;
int32_t y;
} point_t;
If you can get away with using 16bit integers, this will go at twice the speed (because you can fit 8x 16bit numbers in a SIMD register, v.s. 4x 32bit). Whilst we're at it, we might as well change the data layout to structure of array at the same time.
I'm also going to hoist the pointless p.x + width out, and store it as xmax/ymax instead (removes duplicated computation in your loops).
typedef struct rect_x8_t {
int16x8_t x;
int16x8_t y;
int16x8_t xmax; //< x + width
int16x8_t ymax; //< y + height
} rect_x8_t;
typedef struct point_x8_t {
int16x8_t x;
int16x8_t y;
} point_x8_t;
On the assumption you don't have a number of clips that's divisible by 8, we'll need to pad the number slightly (not a big deal)
// assuming this has already been initialised
rect_t rs[CLIPS];
// how many batches of 8 do we need?
uint32_t CLIPS8 = (CLIPS / 8) + (CLIPS & 7 ? 1 : 0);
// allocate in batches of 8
rect_x8_t rs8[CLIPS8] = {};
// I'm going to do this rubbishly as an pre-process step.
// I don't care too much about efficiency here...
for(uint32_t i = 0; i < CLIPS; ++i) {
rs8[i / 8].x[i & 7] = rs[i].x;
rs8[i / 8].y[i & 7] = rs[I].y;
rs8[i / 8].xmax[i & 7] = rs[i].x + rs[i].width;
rs8[i / 8].ymax[i & 7] = rs[i].y + rs[i].height;
}
I have a couple of concerns here:
for(i=0;i<HEIGHT;i++) {
for(j=0;j<WIDTH;j++) {
// This seems wrong? Shouldn't it be p = {j, i} ?
point_t p={i, j};
int32_t idx=point_in(&p, rs, CLIPS);
// I'm not quite sure what the result says about your
// image data and clip regions???
//
// This seems like a really silly way of asking
// a simple question about the clip regions. The pixels
// don't have any effect here.
if(idx >= 0)
res[0][idx] = 1;
}
}
Anyhow, now refactoring the point_in method to use int16x8_t, we get:
inline int32_t point_in_x8(const point_x8_t pt,
const rect_x8_t* rs,
uint32_t len) {
for(int32_t i = 0; i < len; i++) {
// perform comparisons on 8 rects at a time
uint16x8_t cmp0 = vcgeq_s16(pt.x, rs[i].x);
uint16x8_t cmp1 = vcgeq_s16(pt.y, rs[i].y);
uint16x8_t cmp2 = vcltq_s16(pt.x, rs[i].xmax);
uint16x8_t cmp3 = vcltq_s16(pt.y, rs[I].ymax);
// combine to single comparison value
uint16x8_t cmp01 = vandq_u16(cmp0, cmp1);
uint16x8_t cmp23 = vandq_u16(cmp2, cmp3);
uint16x8_t cmp0123 = vandq_u16(cmp01, cmp23);
// use a horizontal max to see if any lanes are true
if(vmaxvq_u16(cmp0123)) {
for(int32_t j = 0; j < 8; ++j) {
if(cmp0123[j])
return 8*i + j;
}
}
}
return -1;
}
Any additional padded elements in the rect_x8_t structs should end up being ignored (since they should be 0/0, 0/0, which will always end up being false).
Then finally...
for(i = 0; i < HEIGHT; i++) {
point_x8_t p;
// splat the y value
p.y = vld1q_dup_s16(i);
for(j = 0; j < WIDTH; j++) {
// splat the x value
p.x = vld1q_dup_s16(j);
int32_t idx = point_in_x8(p, rs8, CLIPS8);
if(idx >= 0)
res[1][idx] = 1;
}
}
The vld4 instruction actually has a fairly high latency. Given that WIDTH * HEIGHT is actually a very big number, pre-swizzling here (as a pre-processing step) makes a lot more sense imho.
HOWEVER
This whole algorithm could be massively improved by simply ignoring the pixels, and working on CLIP regions directly.
A clip region will be false if it is entirely contained by the preceding clip regions
for(i = 0; i < CLIPS; i++) {
// if region is empty, ignore.
if(rs[i].width == 0 || rs[i].height == 0) {
res[0][i] = 0;
continue;
}
// first region will always be true (unless it's of zero size)
if(i == 0) {
res[0][1] = 1;
continue;
}
uint32_t how_many_intersect = 0;
bool entirely_contained = false;
uint32_t intersection_indices[CLIPS] = {};
// do a lazy test first.
for(j = i - 1; j >= 0; --j) {
// if the last region is entirely contained by preceding
// ones, it will be false. exit loop.
if(region_is_entirely_contained(rs[i], rs[j])) {
res[0][i] = 0;
entirely_contained = true;
j = -1; ///< break out of loop
}
else
// do the regions intersect?
if(region_intersects(rs[i], rs[j])) {
intersection_indices[how_many_intersect] = j;
++how_many_intersect;
}
}
// if one region entirely contains this clip region, skip it.
if(entirely_contained) {
continue;
}
// if you only intersect one or no regions, the result is true.
if(how_many_intersect <= 1) {
res[0][i] = 1;
continue;
}
// If you get here, the result is *probably* true, however
// you will need to split this clip region against the previous
// ones to be fully sure. If all regions are fully contained,
// the answer is false.
// I won't implement it, but something like this:
* split rs[i] against each rs[intersection_indices[]].
* Throw away the rectangles that are entirely contained.
* Each bit that remains should be tested against each rs[intersection_indices[]]
* If you find any split rectangle that isn't contained,
set to true and move on.
}
I am working with some C code and I'm totally stuck in this function. It should compare two buffers with some deviator. For example if EEPROM_buffer[1] = 80, so TxBuffer values from 78 to 82 should be correct!
So the problem is that it always returns -1. I checked both buffers, data is correct and they should match, but won't. Program just runs while until reach i = 3 and returns -1..
I compile with atmel studio 6.1, atmel32A4U microcontroller..
int8_t CheckMatching(t_IrBuff * tx_buffer, t_IrBuff * tpool)
{
uint8_t i = 0;
uint16_t * TxBuffer = (uint16_t*) tx_buffer->data;
while((TxBuffer->state != Data_match) || (i != (SavedBuff_count))) // Data_match = 7;
{
uint16_t * EEPROM_buffer = (uint16_t*) tpool[i].data;
for(uint16_t j = 0; j < tpool[i].usedSize; j++) // tpool[i].usedSize = 67;
{
if(abs(TxBuffer[j] - EEPROM_buffer[j]) > 3)
{
i++;
continue;
}
}
i++;
TxBuffer->state = Data_match; // state value before Data_match equal 6!
}
tx_buffer->state = Buffer_empty;
if(i == (SavedBuff_count)) // SavedBuff_count = 3;
{
return -1;
}
return i;
}
Both your TxBuffer elements and EEPROM_buffer elements are uint16_t. When deducting 81 from 80 as uint16_t it would give 0xffff, with no chance of abs to help you. Do a typecast to int32_t and you will be better off.
For a game in Gameboy programming, I am using four arrays called top, oldTop, bottom and oldBottom:
struct Point { int x, y; };
struct Rect { struct Point xx, yy; };
Rect top[size], oldTop[size];
Rect bottom[size], oldBottom[i];
where Rect is a struct made of two Struct Points, the top-left and the bottom right corner points.
The idea of the game is to have random-heighted blocks top-down from the ceiling and bottom-up from the floor.
It is similar to the copter-classic game. In my infinite while loop, I shift all of the rectangles down by one pixel using the following code
while (1)
{
for (int i = 0; i < size; i++)
{
//in Struct Rect, xx is the top-left corner point, and yy is the bottom right
top[i].xx.x--;
top[i].yy.x--;
bottom[i].xx.x--;
bottom[i].yy.x--;
if (top[i].xx.x < 0)
{
top[i].xx.x += 240;
top[i].yy.x += 240;
}
if (bottom[i].xx.x < 0)
{
bottom[i].xx.x += 240;
bottom[i].yy.x += 240;
}
}
for (int i = 0; i < size; i++)
{
drawRect(oldTop[i], colorBlack);
drawRect(oldBottom[i], colorBlack);
}
/*call delay function that wait for Vertical Blank*/
for(int i = 0; i < size; i++)
{
drawRect(top[i], colorGreen);
drawRect(bottom[i], colorGreen);
oldTop[i] = top[i];
oldBottom[i] = bottom[i];
}
}
The drawRect method uses DMA to draw the rectangle.
with this code, the code should display the rectangles like this: (drew this up in paint)
But the result I get is
What is odd is that if I don't draw the bottom row at all, then the top row draws fine. The result only messes up when I draw both. This is really weird because I think that the code should be working fine, and the code is not very complicated. Is there a specific reason this is happening, and is there a way to remedy this?
Thanks.
The code that I use to draw the rectangle looks like this:
void drawRect(int row, int col, int width, int height){
int i;
for (i=0; i<height; i++)
{
DMA[3].src = &color;
DMA[3].dst = videoBuffer + (row+r)*240 + col);
DMA[3].cnt = DMA_ON | DMA_FIXED_SOURCE | width;
}
}
Here's a debugging SSCCE (Short, Self-Contained, Correct Example) based on your code. There are assertions in this code that fire; it runs, but is known not to be correct. I've renamed bottom to btm and oldBottom to oldBtm so that the names are symmetric; it makes the code layout more systematic (but is otherwise immaterial).
#include <assert.h>
#include <stdio.h>
typedef struct Point { int x, y; } Point;
typedef struct Rect { struct Point xx, yy; } Rect;
enum { size = 2 };
typedef enum { colourGreen = 0, colourBlack = 1 } Colour;
/*ARGSUSED*/
static void drawRect(Rect r, Colour c)
{
printf(" (%3d)(%3d)", r.xx.x, r.yy.x);
}
int main(void)
{
Rect top[size], oldTop[size];
Rect btm[size], oldBtm[size];
int counter = 0;
for (int i = 0; i < size; i++)
{
top[i].xx.x = 240 - 4 * i;
top[i].xx.y = 0 + 10 + i;
top[i].yy.x = 240 - 14 * i;
top[i].yy.y = 0 + 20 + i;
btm[i].xx.x = 0 + 72 * i;
btm[i].xx.y = 0 + 10 * i;
btm[i].yy.x = 0 + 12 * i;
btm[i].yy.y = 0 + 20 * i;
oldTop[i] = top[i];
oldBtm[i] = btm[i];
}
while (1)
{
if (counter++ > 480) // Limit amount of output!
break;
for (int i = 0; i < size; i++)
{
//in Struct Rect, xx is the top-left corner point, and yy is the bottom right
top[i].xx.x--;
top[i].yy.x--;
btm[i].xx.x--;
btm[i].yy.x--;
if (top[i].xx.x < 0)
{
top[i].xx.x += 240;
top[i].yy.x += 240;
}
if (btm[i].xx.x < 0)
{
btm[i].xx.x += 240;
btm[i].yy.x += 240;
}
}
for (int i = 0; i < size; i++)
{
assert(top[i].xx.x >= 0 && top[i].yy.x >= 0);
assert(btm[i].xx.x >= 0 && btm[i].yy.x >= 0);
}
for (int i = 0; i < size; i++)
{
drawRect(oldTop[i], colourBlack);
drawRect(oldBtm[i], colourBlack);
}
/*call delay function that wait for Vertical Blank*/
for(int i = 0; i < size; i++)
{
drawRect(top[i], colourGreen);
drawRect(btm[i], colourGreen);
oldTop[i] = top[i];
oldBtm[i] = btm[i];
}
putchar('\n');
}
return(0);
}
As noted in a late comment, one big difference between this and your code is that oldBottom in your code is declared as:
Rect top[size], oldTop[size];
Rect bottom[size], oldBottom[i];
using the size i instead of size. This probably accounts for array overwriting issues you see.
There's a second problem though; the assertions in the loop in the middle fire:
(240)(240) ( 0)( 0) (236)(226) ( 72)( 12) (239)(239) (239)(239) (235)(225) ( 71)( 11)
(239)(239) (239)(239) (235)(225) ( 71)( 11) (238)(238) (238)(238) (234)(224) ( 70)( 10)
(238)(238) (238)(238) (234)(224) ( 70)( 10) (237)(237) (237)(237) (233)(223) ( 69)( 9)
(237)(237) (237)(237) (233)(223) ( 69)( 9) (236)(236) (236)(236) (232)(222) ( 68)( 8)
(236)(236) (236)(236) (232)(222) ( 68)( 8) (235)(235) (235)(235) (231)(221) ( 67)( 7)
(235)(235) (235)(235) (231)(221) ( 67)( 7) (234)(234) (234)(234) (230)(220) ( 66)( 6)
(234)(234) (234)(234) (230)(220) ( 66)( 6) (233)(233) (233)(233) (229)(219) ( 65)( 5)
(233)(233) (233)(233) (229)(219) ( 65)( 5) (232)(232) (232)(232) (228)(218) ( 64)( 4)
(232)(232) (232)(232) (228)(218) ( 64)( 4) (231)(231) (231)(231) (227)(217) ( 63)( 3)
(231)(231) (231)(231) (227)(217) ( 63)( 3) (230)(230) (230)(230) (226)(216) ( 62)( 2)
(230)(230) (230)(230) (226)(216) ( 62)( 2) (229)(229) (229)(229) (225)(215) ( 61)( 1)
(229)(229) (229)(229) (225)(215) ( 61)( 1) (228)(228) (228)(228) (224)(214) ( 60)( 0)
Assertion failed: (btm[i].xx.x >= 0 && btm[i].yy.x >= 0), function main, file video.c, line 63.
I think your 'not negative' checks should be revised to:
if (top[i].xx.x < 0)
top[i].xx.x += 240;
if (top[i].yy.x < 0)
top[i].yy.x += 240;
if (btm[i].xx.x < 0)
btm[i].xx.x += 240;
if (btm[i].yy.x < 0)
btm[i].yy.x += 240;
This stops anything going negative. However, it is perfectly plausible that you should simply be checking on the bottom-right x-coordinate (instead of the top-left coordinate) using the original block. Or the wraparound may need to be more complex altogether. That's for you to decipher. But I think that the odd displays occur because you were providing negative values where you didn't intend to and weren't supposed to.
The key points to note here are:
When you're debugging an algorithm, you don't have to use the normal display mechanisms.
When you're debugging, reduce loop sizes where you can (size == 2).
Printing just the relevant information (here, the x-coordinates) helped reduce the output.
Putting the counter code to limit the amount of output simplifies things.
If things are going wrong, look for patterns in what is going wrong early.
I had various versions of the drawRect() function before I got to the design shown, which works well on a wide screen (eg 120x65) terminal window.
I have seen ctk.c obfuscated code, but How can I start to de-obfuscate it?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#define m(b)a=b;z=*a;while(*++a){y=*a;*a=z;z=y;}
#define h(u)G=u<<3;printf("\e[%uq",l[u])
#define c(n,s)case n:s;continue
char x[]="((((((((((((((((((((((",w[]=
"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";char r[]={92,124,47},l[]={2,3,1
,0};char*T[]={" |"," |","%\\|/%"," %%%",""};char d=1,p=40,o=40,k=0,*a,y,z,g=
-1,G,X,**P=&T[4],f=0;unsigned int s=0;void u(int i){int n;printf(
"\233;%uH\233L%c\233;%uH%c\233;%uH%s\23322;%uH#\23323;%uH \n",*x-*w,r[d],*x+*w
,r[d],X,*P,p+=k,o);if(abs(p-x[21])>=w[21])exit(0);if(g!=G){struct itimerval t=
{0,0,0,0};g+=((g<G)<<1)-1;t.it_interval.tv_usec=t.it_value.tv_usec=72000/((g>>
3)+1);setitimer(0,&t,0);f&&printf("\e[10;%u]",g+24);}f&&putchar(7);s+=(9-w[21]
)*((g>>3)+1);o=p;m(x);m(w);(n=rand())&255||--*w||++*w;if(!(**P&&P++||n&7936)){
while(abs((X=rand()%76)-*x+2)-*w<6);++X;P=T;}(n=rand()&31)<3&&(d=n);!d&&--*x<=
*w&&(++*x,++d)||d==2&&++*x+*w>79&&(--*x,--d);signal(i,u);}void e(){signal(14,
SIG_IGN);printf("\e[0q\ecScore: %u\n",s);system("stty echo -cbreak");}int main
(int C,char**V){atexit(e);(C<2||*V[1]!=113)&&(f=(C=*(int*)getenv("TERM"))==(
int)0x756E696C||C==(int)0x6C696E75);srand(getpid());system("stty -echo cbreak"
);h(0);u(14);for(;;)switch(getchar()){case 113:return 0;case 91:case 98:c(44,k
=-1);case 32:case 110:c(46,k=0);case 93:case 109:c(47,k=1);c(49,h(0));c(50,h(1
));c(51,h(2));c(52,h(3));}}
http://www.ioccc.org/2001/ctk.hint:
This is a game based on an Apple ][ Print Shop Companion easter
egg named 'DRIVER', in which the goal is to drive as fast as
you can down a long twisty highway without running off the
road. Use ',./', '[ ]', or 'bnm' to go left, straight, and
right respectively. Use '1234' to switch gears. 'q' quits. The
faster you go and the thinner the road is, the more points you
get. Most of the obfuscation is in the nonsensical if statements
among other things. It works best on the Linux console: you
get engine sound (!) and the * Lock keyboard lights tell you
what gear you're in (none lit=4th). The 'q' argument (no
leading '-') will silence the sound. It won't work on a terminal
smaller than 80x24, but it works fine with more (try it in an
XTerm with the "Unreadable" font and the window maximized
vertically!).
1st step
Using:
sed -e'/#include/d' ctk.c | gcc -E - | sed -e's/;/;\n/g' -e's/}/}\n/g' -e '/^#/d' | indent
I was able to generate the following output which while not perfect already seems to be readable a lot better:
char x[] = "((((((((((((((((((((((", w[] =
"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
char r[] = { 92, 124, 47 }
, l[] =
{
2, 3, 1, 0}
;
char *T[] = { " |", " |", "%\\|/%", " %%%", "" }
;
char d = 1, p = 40, o = 40, k = 0, *a, y, z, g = -1, G, X, **P = &T[4], f = 0;
unsigned int s = 0;
void
u (int i)
{
int n;
printf ("\233;
%uH\233L%c\233;
%uH%c\233;
%uH%s\23322;
%uH#\23323;
%uH \n", *x - *w, r[d], *x + *w, r[d], X, *P, p += k, o);
if (abs (p - x[21]) >= w[21])
exit (0);
if (g != G)
{
struct itimerval t = { 0, 0, 0, 0 }
;
g += ((g < G) << 1) - 1;
t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((g >> 3) + 1);
setitimer (0, &t, 0);
f && printf ("\e[10;
%u]", g + 24);
}
f && putchar (7);
s += (9 - w[21]) * ((g >> 3) + 1);
o = p;
a = x;
z = *a;
while (*++a)
{
y = *a;
*a = z;
z = y;
}
;
a = w;
z = *a;
while (*++a)
{
y = *a;
*a = z;
z = y;
}
;
(n = rand ()) & 255 || --*w || ++*w;
if (!(**P && P++ || n & 7936))
{
while (abs ((X = rand () % 76) - *x + 2) - *w < 6);
++X;
P = T;
}
(n = rand () & 31) < 3 && (d = n);
!d && --*x <= *w && (++*x, ++d) || d == 2 && ++*x + *w > 79 && (--*x, --d);
signal (i, u);
}
void
e ()
{
signal (14, SIG_IGN);
printf ("\e[0q\ecScore: %u\n", s);
system ("stty echo -cbreak");
}
int main (int C, char **V)
{
atexit (e);
(C < 2 || *V[1] != 113)
&& (f = (C = *(int *) getenv ("TERM")) == (int) 0x756E696C
|| C == (int) 0x6C696E75);
srand (getpid ());
system ("stty -echo cbreak");
G = 0 << 3;
printf ("\e[%uq", l[0]);
u (14);
for (;;)
switch (getchar ())
{
case 113:
return 0;
case 91:
case 98:
case 44:
k = -1;
continue;
case 32:
case 110:
case 46:
k = 0;
continue;
case 93:
case 109:
case 47:
k = 1;
continue;
case 49:
G = 0 << 3;
printf ("\e[%uq", l[0]);
continue;
case 50:
G = 1 << 3;
printf ("\e[%uq", l[1]);
continue;
case 51:
G = 2 << 3;
printf ("\e[%uq", l[2]);
continue;
case 52:
G = 3 << 3;
printf ("\e[%uq", l[3]);
continue;
}
}
... and now?
I don't think there's much more an automated process will be able perform at this point as the term "more" readable or "less" readable from now on might depend on the specific preferences of the reader.
One step that could be performed is removing escape sequences from the strings and placing them somewhere separately. As it turns out the whole
char l[] = {2, 3, 1, 0}
has no other purpose than to be utilized in the escape sequences of the main loop:
printf ("\e[%uq", l[0]);
and so on. Looking up their meaning:
ESC [ 0 q: clear all LEDs
ESC [ 1 q: set Scroll Lock LED
ESC [ 2 q: set Num Lock LED
ESC [ 3 q: set Caps Lock LED
depending on taste you might want to exchange them with a macro or a function call more meaningful to you like clear_all_LEDs and so on.
I strongly doubt a machine would agree on this being a simplification. As it turns out the whole main loop just seems to be working with keys entered by the user, so probably turning numbers into their corresponding characters might add to readability, like in replacing:
case 113:
return 0;
case 91:
case 98:
case 44:
k = -1;
// ...
case 49:
G = 0 << 3;
printf ("\e[%uq", l[0]);
with something like:
case 'q':
return 0;
case '[':
case 'b':
case ',':
k = -1;
// ...
case '1':
G = 0 << 3;
set_Num_Lock_LED ();
Oh - and while we are at it already why wouldn't we want to change the name from this rather strange G to gear. Again I strongly doubt an automated process would have found renaming from G to gear any better than renaming it to butterfly. Well maybe it even isn't.
While beautifying names maybe this function referenced by a single u is another candidate:
u (14);
with a more meaningful name update probably. And as we already included <signal.h> why don't we deobfuscate the code further by replacing 14 with SIGALRM like this:
upadate (SIGALRM);
As you can see "deobfuscating" here requires the exact opposite step of that taken before. Replacing the expansion with a macro this time. How would a machine try to decide which one is more useful?
Another spot where we might want to replace a bare number with something else is this one in the update function:
f && putchar (7);
Why not replace the 7 with \a as it will turn out to be the same in the end. Maybe we should even change the bare f with something more "meaningful".
Again I vote agains butterfly but would rather like to call it play_sound:
if (play_sound)
putchar ('\a');
might be the more readable version we are looking for. Sure we shouldn't forget to replace f in all other spots. The one right at the beginning of our main function beeing such a culprit:
Holy mess
int main (int C, char **V)
{
atexit (e);
(C < 2 || *V[1] != 113)
&& (f = (C = *(int *) getenv ("TERM")) == (int) 0x756E696C
|| C == (int) 0x6C696E75);
While happily renaming f to play_sound and e to - no, still no butterfly, this time I'll rather call it: - end we spot that the function signature seems to look a bit strange in terms of naming conventions: argc instead of C and argv instead of V would seem more conventional here. Thus giving us:
int main (int argc, char* argv[])
{
atexit (end);
(argc < 2 || *argv[1] != 113)
&& (playsound = (argc = *(int *) getenv ("TERM")) == (int) 0x756E696C
|| argc == (int) 0x6C696E75);
As this is still not a beauty we ask our standards guy and he informs us that it's pretty OK to replace
(A || B) && (C)
with
if (A || B) { C }
and
E = (x=F)==H || x==I
with
x = F;
if (x==H || x==I)
A=1;
else
A=0;`
So maybe this should be a more readable version of the whole code:
if (argc < 2 || *argv[1] != 'q') {
argc = *(int*) getenv ("TERM");
if (argc == (int) 0x756E69 || argc == (int) 0x6C696E75))
play_sound = 1;
/* skip the else brach here as play_sound is alredy initialized to 0 */
}
Now still another guy turns up and starts to inform us, that depending on something called endianness tose strange looking numbers 0x6C696E75 and 0x756E69 if stored in memory would (when interpreting raw byte vales as ASCII code) just look like "linu" or "unil". One being "unil" on one architecure type and "linu" the other one while just the other way round on the other architecture with different endianness.
So taking a closer look what's essentially happening here is:
we get a pointer to a string from getenv ("TERM") which we typcast to a pointer to an int before dereferencing it thus leading the bit pattern stored at the string location as an int.
next we compare this value with the one we would get if had performed the same with either "unil" or "linu" stored at that specific location.
Probably we just want to check if the TERM environment variable is set to "linux" so our deobfuscated version might want to perform a string comparison here.
As on the other hand we can't be sure if also allowing terminals with names starting with "unil" to play sound might be a special feature of this software so I decided to probably better leave it intact.
What now ?
While renaming and re-encoding variable names and values those strange char arrays could be our next victims. The following mess doesn't look too nice:
char x[] = "((((((((((((((((((((((", w[] =
"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
char r[] = { 92, 124, 47 };
So maybe they could be changed to:
char x_offset[] = {
40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
40, 40, 0 };
char width[] = {
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 0 };
const char border[] = "\\|/";
As you can see I just chose to switch the way the values are described between x as string constant to x written down as an array as this way the purpose of the values stored here seemed a little bit clearer to me.
While on the other hand I changed the way the way r is written down just in exactly the opposite direction as again this seemed a lot clearer to me.
While hunting down all those refs to x, w and r the time could be used to rename p and o to - sorry again no butterfly - pos and old_pos while renaming s to score.
Changing for example:
s += (9 - w[21]) * ((g >> 3) + 1);
o = p;
a = x;
z = *a;
while (*++a)
{
y = *a;
*a = z;
z = y;
}
;
a = w;
z = *a;
while (*++a)
{
y = *a;
*a = z;
z = y;
}
;
to:
/* update score */
score += (9 - width[NEXT_LINE]) * ((g >> 3) + 1);
old_pos = pos;
/* shift x_offset */
a = x_offset;
z = *a;
while (*++a) {
y = *a;
*a = z;
z = y;
};
/* shift width */
a = width;
z = *a;
while (*++a) {
y = *a;
*a = z;
z = y;
};
Besides the possibility to turn it into some other kind of loop there's not much beautification possible for both shifting functions so probably adding an appropriate comment is the maximum you can do. Removing the magic number 21 might be another idea NEXT_LINE didn't seem to be the worst choice here.
The single character labeled variable g still doesn't look too good. But renaming it to something like update_interval there's also the chance to eliminate another weird terminal escape sequence:
if (g != G)
{
struct itimerval t = { 0, 0, 0, 0 }
;
g += ((g < G) << 1) - 1;
t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((g >> 3) + 1);
setitimer (0, &t, 0);
f && printf ("\e[10;
%u]", g + 24);
}
Maybe looks a little bit more confusing than:
/* update simulation speed */
if (update_interval != gear) {
struct itimerval t = { 0, 0, 0, 0 } ;
update_interval += ((update_interval < gear) << 1) - 1;
t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((update_interval >> 3) + 1);
setitimer (0, &t, 0);
if (play_sound)
change_bell_frequency (update_interval + 24);
}
Last fixes
Although the code should look a lot more readable by now there are still some nasty parts left:
!d && --*x <= *w && (++*x, ++d) || d == 2 && ++*x + *w > 79 && (--*x, --d);
Choosing another (hopefully) more meaningful name for d and breaking operator precedence down you might end up with something like:
if (curve == CURVE_LEFT) {
--*x_offset;
if (*x_offset < *width) {
++*x_offset;
curve = CURVE_NONE;
}
}
else if (curve == CURVE_RIGHT) {
++*x_offset;
if (*x_offset + *width > 79) {
--*x_offsett;
curve = CURVE_NONE;
}
}
instead adding appropriate macros for all those CURVE_...s.
Now there are still those X, P and T names hanging around that also might be changed. As it makes its purpose also a little bit better visible in code I decided to flip the line order of T that I renamed to tree which sure means the calculation also has to be fixed. All in all it's from:
char *T[] = { " |", " |", "%\\|/%", " %%%", "" };
char X, **P = &T[4];
// ...
if (!(**P && P++ || n & 7936))
{
while (abs ((X = rand () % 76) - *x + 2) - *w < 6);
++X;
P = T;
}
To something like:
char *tree[] = {
"",
" %%%",
"%\\|/%",
" |",
" |",
};
char **tree_line = tree;
char tree_position;
// ...
/* update tree line pointer */
if (!(**tree_line && tree_line-- || n & 7936)) {
/* find the right spot to grow */
while (abs ((tree_position = rand () % 76) - *x_offset + 2) - *width < 6)
;
++tree_position;
tree_line = &tree[4];
}
Keeping the best part until the end
Although the code already seems to looks a lot prettier to me now there's still one part missing. That's the one that's doing all the output. It's this line I'm talking about:
printf ("\233;%uH\233L%c\233;%uH%c\233;%uH%s\23322;%uH#\23323;%uH \n",
*x - *w, r[d], *x + *w, r[d], X, *P, p += k, o);
That apart from looking pretty hard to read was even to obfuscated for computer to produce any usable result.
I tried a lot of different things running in other terminal emulators, changing terminal settings and switching locales back and forth without sucess.
So besides the fact this kind of obfuscation seemed to be more that perfect as it even seems to confuse my computer I still can't tell what trick the author intended here.
The octal code \233 has the same bit-pattern as the escape character (\033) with the 8-th bit set additionally which probably is in some way related to effect that was intended here. Unfortunately as I already told it didn't work for me.
Fortunately enough the escape sequences still seemed easy enough to guess, so I came up with the following replacement:
pos += move_x,
/* draw street */
printf ("\e[1;%uH" "\e[L" "%c"
"\e[1;%uH" "%c",
*x_offset - *width, border[curve],
*x_offset + *width, border[curve]);
/* draw tree */
printf ("\e[1;%uH" "%s",
tree_position, *tree_line);
/* redraw car */
printf ("\e[22;%uH" "#"
"\e[23;%uH" " " "\n",
pos,
old_pos);
Taking drawing down into separate to (hopefully) make them a little bit more readable. The actual line and the previous line are still hard coded here as in the original version. Maybe extracting them from there as shown below would even improve readability:
/* draw street */
printf ("\e[1;%uH" "\e[L" "%c"
"\e[1;%uH" "%c",
*x_offset - *width, border[curve],
*x_offset + *width, border[curve]);
/* draw tree */
printf ("\e[1;%uH" "%s",
tree_position, *tree_line);
/* redraw car */
printf ("\e[%u;%uH" "#"
"\e[%u;%uH" " " "\n",
NEXT_LINE +1, pos,
NEXT_LINE +2, old_pos);
This finally brought me to the first usable version which I then "tested" a lot. While probably not 100% state of the art it still seems to be very addictive.
Last words
Here the final unobfuscated version that I came with. As you'll see I didn't implement the LED setting functions and the clear screen function but it shouldn't be to hard to find the needed escape sequences scattered throughout the obfuscated version. In fact I already mentioned the LED sequences in this post. The one to clear the screen is "\e[0q". Happy hacking.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
#define NEXT_LINE 21
#define CURVE_LEFT 0
#define CURVE_NONE 1
#define CURVE_RIGHT 2
char x_offset[] = {
40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
40, 40, 0 };
char width[] = {
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 0 };
const char border[] = "\\|/";
void change_bell_frequency () {}
void clear_screen () {}
void clear_all_LEDs () {}
void set_Num_Lock_LED () {}
void set_Scroll_lock_LED () {}
void set_Caps_Lock_LED () {}
char *tree[] = {
"",
" %%%",
"%\\|/%",
" |",
" |",
};
char **tree_line = tree;
char tree_position;
char curve = CURVE_NONE;
char *a, y, z;
char move_x = 0;
char update_interval = -1;
char pos = 40;
char old_pos = 40;
char play_sound = 0;
char gear;
unsigned int score = 0;
void move (char x, char y) {
printf ("\e[%u;%uH", x, y);
}
void insert () {
printf ("\e[L");
}
void update (int i) {
int n;
pos += move_x,
/* draw street */
printf ("\e[1;%uH" "\e[L" "%c"
"\e[1;%uH" "%c",
*x_offset - *width, border[curve],
*x_offset + *width, border[curve]);
/* draw tree */
printf ("\e[1;%uH" "%s",
tree_position, *tree_line);
/* redraw car */
printf ("\e[%u;%uH" "#"
"\e[%u;%uH" " " "\n",
NEXT_LINE + 1, pos,
NEXT_LINE +2, old_pos);
/* did we leave the road ? */
if (abs (pos - x_offset[NEXT_LINE]) >= width[NEXT_LINE])
exit (0);
/* update simulation speed */
if (update_interval != gear) {
struct itimerval t = { 0, 0, 0, 0 } ;
update_interval += ((update_interval < gear) << 1) - 1;
t.it_interval.tv_usec = t.it_value.tv_usec = 72000 / ((update_interval >> 3) + 1);
setitimer (0, &t, 0);
if (play_sound)
change_bell_frequency (update_interval + 24);
}
/* play sound */
if (play_sound)
putchar ('\a');
/* update score */
score += (9 - width[NEXT_LINE]) * ((update_interval >> 3) + 1);
old_pos = pos;
/* shift x_offset */
a = x_offset;
z = *a;
while (*++a) {
y = *a;
*a = z;
z = y;
};
/* shift width */
a = width;
z = *a;
while (*++a) {
y = *a;
*a = z;
z = y;
};
/* generate new road */
n = rand ();
if (!(n & 255) && *width > 1)
--*width;
/* set tree line pointer */
if (!(**tree_line && tree_line-- || n & 7936)) {
/* find the right spot to grow */
while (abs ((tree_position = rand () % 76) - *x_offset + 2) - *width < 6)
;
++tree_position;
tree_line = &tree[4];
}
/* new offset */
n = rand () & 31;
if (n < 3)
curve = n;
if (curve == CURVE_LEFT) {
--*x_offset;
if (*x_offset <= *width) {
++*x_offset;
curve = CURVE_NONE;
}
}
else if (curve == CURVE_RIGHT) {
++*x_offset;
if (*x_offset + *width > 79) {
--*x_offset;
curve = CURVE_NONE;
}
}
signal (SIGALRM, update);
}
void end () {
signal (SIGALRM, SIG_IGN);
clear_all_LEDs ();
clear_screen ();
printf ("Score: %u\n", score);
system ("stty echo -cbreak");
}
int main (int argc, char **argv) {
atexit (end);
if (argc < 2 || *argv[1] != 'q') {
argc = *(int*) getenv ("TERM");
if (argc == (int) 0x6C696E75 || argc == (int) 0x756E696C)
play_sound = 1;
}
srand (getpid ());
system ("stty -echo cbreak");
gear = 0 << 3;
clear_all_LEDs ();
update (14);
for (;;)
switch (getchar ())
{
case 'q':
return 0;
case '[':
case 'b':
case ',':
move_x = -1;
continue;
case ' ':
case 'n':
case '.':
move_x = 0;
continue;
case ']':
case 'm':
case '/':
move_x = 1;
continue;
case '1':
gear = 0 << 3;
set_Num_Lock_LED ();
continue;
case '2':
gear = 1 << 3;
set_Caps_Lock_LED ();
continue;
case '3':
gear = 2 << 3;
set_Scroll_lock_LED ();
continue;
case '4':
gear = 3 << 3;
clear_all_LEDs ();
continue;
}
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I saw this code in http://www.cise.ufl.edu/~manuel/obfuscate/obfuscate.html (http://www.cise.ufl.edu/~manuel/obfuscate/savastio) website.But this code is very very crazy. I could not able to understand what is happening when it is happening.
Please help me. Let me know what is happening when each line is getting executed.
e.g. printf function is there, if we will see there is no statement called as "Enter the number". But still while executing it is asking "Enter the number". From where it is comming.
And why the code is written like this. It looks like "n!"(Though it means factorial).
Please help me.
#include <stdio.h>
#define l11l 0xFFFF
#define ll1 for
#define ll111 if
#define l1l1 unsigned
#define l111 struct
#define lll11 short
#define ll11l long
#define ll1ll putchar
#define l1l1l(l) l=malloc(sizeof(l111 llll1));l->lll1l=1-1;l->ll1l1=1-1;
#define l1ll1 *lllll++=l1ll%10000;l1ll/=10000;
#define l1lll ll111(!l1->lll1l){l1l1l(l1->lll1l);l1->lll1l->ll1l1=l1;}\
lllll=(l1=l1->lll1l)->lll;ll=1-1;
#define llll 1000
l111 llll1 {
l111 llll1 *
lll1l,*ll1l1 ;l1l1 lll11 lll [
llll];};main (){l111 llll1 *ll11,*l1l,*
l1, *ll1l, * malloc ( ) ; l1l1 ll11l l1ll ;
ll11l l11,ll ,l;l1l1 lll11 *lll1,* lllll; ll1(l
=1-1 ;l< 14; ll1ll("\t\"8)>l\"9!.)>vl" [l]^'L'),++l
);scanf("%d",&l);l1l1l(l1l) l1l1l(ll11 ) (l1=l1l)->
lll[l1l->lll[1-1] =1]=l11l;ll1(l11 =1+1;l11<=l;
++l11){l1=ll11; lll1 = (ll1l=( ll11=l1l))->
lll; lllll =( l1l=l1)->lll; ll=(l1ll=1-1
);ll1(;ll1l-> lll1l||l11l!= *lll1;){l1ll
+=l11**lll1++ ;l1ll1 ll111 (++ll>llll){
l1lll lll1=( ll1l =ll1l-> lll1l)->lll;
}}ll1(;l1ll; ){l1ll1 ll111 (++ll>=llll)
{ l1lll} } * lllll=l11l;}
ll1(l=(ll=1- 1);(l<llll)&&
(l1->lll[ l] !=l11l);++l); ll1 (;l1;l1=
l1->ll1l1,l= llll){ll1(--l ;l>=1-1;--l,
++ll)printf( (ll)?((ll%19) ?"%04d":(ll=
19,"\n%04d") ):"%4d",l1-> lll[l] ) ; }
ll1ll(10); }
As a start, you can get the code indented properly and remove the indirection caused by the #defines. The indentation can be done by GNU indent and gcc -E will do the preprocessing. Assuming the code is in factorial.c (this requires using the command line):
> gcc -E factorial.c | indent > clean_factorial.c
A thing to note is the preprocessing step will dump all of stdio.h into clean_factorial.c; but that is irrelevant information, so we should comment out/delete #include<stdio.h> before running gcc -E. This basically gives:
struct llll1
{
struct llll1 *lll1l, *ll1l1;
unsigned short lll[1000];
};
main ()
{
struct llll1 *ll11, *l1l, *l1, *ll1l, *malloc ();
unsigned long l1ll;
long l11, ll, l;
unsigned short *lll1, *lllll;
for (l = 1 - 1; l < 14; putchar ("\t\"8)>l\"9!.)>vl"[l] ^ 'L'), ++l);
scanf ("%d", &l);
l1l = malloc (sizeof (struct llll1));
l1l->lll1l = 1 - 1;
l1l->ll1l1 = 1 - 1;
ll11 = malloc (sizeof (struct llll1));
ll11->lll1l = 1 - 1;
ll11->ll1l1 = 1 - 1;
(l1 = l1l)->lll[l1l->lll[1 - 1] = 1] = 0xFFFF;
for (l11 = 1 + 1; l11 <= l; ++l11)
{
l1 = ll11;
lll1 = (ll1l = (ll11 = l1l))->lll;
lllll = (l1l = l1)->lll;
ll = (l1ll = 1 - 1);
for (; ll1l->lll1l || 0xFFFF != *lll1;)
{
l1ll += l11 ** lll1++;
*lllll++ = l1ll % 10000;
l1ll /= 10000;
if (++ll > 1000)
{
if (!l1->lll1l)
{
l1->lll1l = malloc (sizeof (struct llll1));
l1->lll1l->lll1l = 1 - 1;
l1->lll1l->ll1l1 = 1 - 1;;
l1->lll1l->ll1l1 = l1;
}
lllll = (l1 = l1->lll1l)->lll;
ll = 1 - 1;
lll1 = (ll1l = ll1l->lll1l)->lll;
}
}
for (; l1ll;)
{
*lllll++ = l1ll % 10000;
l1ll /= 10000;
if (++ll >= 1000)
{
if (!l1->lll1l)
{
l1->lll1l = malloc (sizeof (struct llll1));
l1->lll1l->lll1l = 1 - 1;
l1->lll1l->ll1l1 = 1 - 1;;
l1->lll1l->ll1l1 = l1;
}
lllll = (l1 = l1->lll1l)->lll;
ll = 1 - 1;
}
}
*lllll = 0xFFFF;
}
for (l = (ll = 1 - 1); (l < 1000) && (l1->lll[l] != 0xFFFF); ++l);
for (; l1; l1 = l1->ll1l1, l = 1000)
{
for (--l; l >= 1 - 1; --l, ++ll)
printf ((ll) ? ((ll % 19) ? "%04d" : (ll =
19, "\n%04d")) : "%4d",
l1->lll[l]);
}
putchar (10);
}
This is slightly readable, and we can do things like rename variables so that we can tell them apart easily, even if we don't know what they do yet. E.g. this will rename the struct to structure, and call the pointers inside it left and right (you can do this with the find-replace tool in your editor too):
> sed -i 's/llll1/structure/g; s/lll1l/left/g; s/ll1l1/right/g;' clean_factorial.c
(you have to be careful of the order, or the replacement for lll might conflict with lllll, for example).
There are some other easy things to do:
1 - 1 occurs a lot: replace it with 0 (and 1 + 1 too, except replace that with 2 instead of 0).
The line with "\t\"8)>l\"9!.)>vl"[l] ^ 'L' just goes along the string printing each character after xor'ing it with 'L' (work out why! It might help to put the putchar in the body of the for loop, rather than in the comma-statement).
putchar(10) just prints a new line.
Know about the comma operator.
Other than that, it's just a lot of hard work. You can and should use tools like a debugger to trace the flow of execution and work out what is happening where.
Here's my attempt at unwrapping it, got a bit stuck on the prompt logic, so I just initialized longC to zero, and printed out the request for a number manually:
#include <stdio.h>
struct StructName
{
struct StructName *structA, *structB;
unsigned short unsignedShortArrayA[1000];
};
main ()
{
struct StructName *structC, *structD, *structE, *structF, *malloc();
unsigned long unsignedLongA;
long longA, longB, longC = 0;
unsigned short *unsignedShortA, *unsignedShortB;
//for(longC=0; longC< 14; putchar("\t\"8)>longC\"9!.)>vl" [longC]^'longC'),++longC )
//;
printf("%s", "Enter a number: " );
scanf("%d", &longC);
structD = malloc(sizeof(struct StructName));
structD->structA=0;
structD->structB=0;
structC=malloc(sizeof(struct StructName));
structC->structA=0;
structC->structB=0;
(structE=structD)->unsignedShortArrayA[structD->unsignedShortArrayA[0] =1] = 0xFFFF;
for( longA=1+1;longA<=longC;++longA)
{
structE=structC;
unsignedShortA = (structF=( structC=structD))->unsignedShortArrayA;
unsignedShortB =( structD=structE)->unsignedShortArrayA;
longB=(unsignedLongA=0);
for( ; structF->structA || 0xFFFF != *unsignedShortA; )
{
unsignedLongA+=longA**unsignedShortA++;
*unsignedShortB++=unsignedLongA%10000;
unsignedLongA/=10000;
if( ++longB>1000 )
{
if( !structE->structA )
{
structE->structA=malloc(sizeof(struct StructName));
structE->structA->structA=0;
structE->structA->structB=0;
structE->structA->structB=structE;
}
unsignedShortB=(structE=structE->structA)->unsignedShortArrayA;
longB=0;
unsignedShortA=( structF =structF-> structA)->unsignedShortArrayA;
}
}
for( ; unsignedLongA; )
{
*unsignedShortB++=unsignedLongA%10000;
unsignedLongA/=10000;
if( ++longB>=1000 )
{
if( !structE->structA )
{
structE->structA=malloc(sizeof(struct StructName));
structE->structA->structA=0;
structE->structA->structB=0;
structE->structA->structB=structE;
}
unsignedShortB=(structE=structE->structA)->unsignedShortArrayA;
longB=0;
}
}
* unsignedShortB=0xFFFF;
}
for( longC=(longB=1- 1); (longC<1000) && (structE->unsignedShortArrayA[ longC] !=0xFFFF); ++longC )
{
;
}
for( ; structE; structE=structE->structB, longC=1000 )
{
for( --longC; longC>=0; --longC, ++longB)
{
printf( (longB)?((longB%19) ? "%04d" : (longB=19,"\n%04d") ):"%4d",structE-> unsignedShortArrayA[longC] );
}
}
putchar(10);
}
EDIT: Cleaned up a bit.