// Started with ChatGPT, optimized manually. Valgrinded. Thread and Unicode unsafe.
// 300 ms on 7 MB american-english-insane with size 5 on Ryzen 5 3600 with -O3.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> //toupper (replace with ICU case folding)
#include <stdlib.h>
#include <string.h>
#include <ctype.h> //toupper (replace with ICU case folding)
#define MAXW 262144
#define MAXL 32
#define HSIZE 262139
static char **WL; static unsigned WC = 0; static unsigned found = 0;
// HASHTABLE (with linked lists to handle collisions)
typedef struct W { char *w; struct W *n; } W;
static W *H[HSIZE]; // 2 MB
unsigned hfn(const char *s){
unsigned h=5381;for(;*s;)h=((h<<5)+h)+(unsigned char)*s++;return h%HSIZE;
}
int has(const char *s){
for(W*w=H[hfn(s)];w;w=w->n)if(!strcmp(s,w->w))return 1;
return 0;
}
void ins(const char *s){
unsigned h=hfn(s);W*w=malloc(sizeof* w);w->w=strdup(s);w->n=H[h];H[h]=w;
}
typedef struct W { char *w; struct W *n; } W;
static W *H[HSIZE]; // 2 MB
unsigned hfn(const char *s){
unsigned h=5381;for(;*s;)h=((h<<5)+h)+(unsigned char)*s++;return h%HSIZE;
}
int has(const char *s){
for(W*w=H[hfn(s)];w;w=w->n)if(!strcmp(s,w->w))return 1;
return 0;
}
void ins(const char *s){
unsigned h=hfn(s);W*w=malloc(sizeof* w);w->w=strdup(s);w->n=H[h];H[h]=w;
}
static inline void rev(const char *s, char *d) {
size_t n=strlen(s);for(size_t i=0;i<n;++i)d[i]=s[n-1-i];
d[n]=0; // not Unicode safe (replace with ICU reversing)
}
size_t n=strlen(s);for(size_t i=0;i<n;++i)d[i]=s[n-1-i];
d[n]=0; // not Unicode safe (replace with ICU reversing)
}
void search(int lvl, char **sq, int N){
if(lvl==N/2){ //check square symmetry, not Unicode safe
for(int r=0;r<N;++r) for(int c=0;c<N;++c) if(sq[r][c]!=sq[c][r]) return;
puts(""); for (int i=0; i<N; ++i) puts(sq[i]); //output if fine
++found;
}else{ //O(n) traversing WL & then O(1) looking up reversations in W
for(unsigned i=0; i<WC; ++i){ //unparallelizable memory stingy design
sq[lvl]=WL[i]; //direct to the word for that level and position
if(sq[lvl][0]!=sq[0][lvl]) continue; //acrostic upper left corner
// not Unicode safe
if(N%2&&sq[lvl][N/2]!=sq[N/2][lvl]) continue; //center column
rev(sq[lvl],sq[N-1-lvl]); //reverse row #lvl to row #lvl from bottom
if(!has(sq[N-1-lvl])) continue; //check if reversed thing is real
search(lvl+1,sq,N); //next level
}
}
}
if(lvl==N/2){ //check square symmetry, not Unicode safe
for(int r=0;r<N;++r) for(int c=0;c<N;++c) if(sq[r][c]!=sq[c][r]) return;
puts(""); for (int i=0; i<N; ++i) puts(sq[i]); //output if fine
++found;
}else{ //O(n) traversing WL & then O(1) looking up reversations in W
for(unsigned i=0; i<WC; ++i){ //unparallelizable memory stingy design
sq[lvl]=WL[i]; //direct to the word for that level and position
if(sq[lvl][0]!=sq[0][lvl]) continue; //acrostic upper left corner
// not Unicode safe
if(N%2&&sq[lvl][N/2]!=sq[N/2][lvl]) continue; //center column
rev(sq[lvl],sq[N-1-lvl]); //reverse row #lvl to row #lvl from bottom
if(!has(sq[N-1-lvl])) continue; //check if reversed thing is real
search(lvl+1,sq,N); //next level
}
}
}
void square(int N) {
if (N < 1) return;
unsigned c = 0;
char **sq=calloc(N,sizeof(char*));
// 0 1 N/2 N-2 N-1
// 0 1 2 N/2 N-2 N-1
for(int i=N/2+N%2;i<N;++i) sq[i]=calloc(N+1,sizeof(char));
if(N%2){ //top half needn't to be allocated if using existing WL pointers
for (c = 0; c < WC; ++c) {
int pal = 1;
for(int i=0;i<N/2;i++) if(WL[c][i]!=WL[c][N-1-i]){pal=0;break;}
if(!pal) continue; // not Unicode safe
sq[N/2]=WL[c];
search(0,sq,N);
}
}else{
search(0,sq,N);
}
for(int i=N/2+N%2;i<N;++i) free(sq[i]);
free(sq);
}
int main(int c,char**v){
if(c<3){fprintf(stderr,"\nUsage: %s <wordlist> <size>\n",v[0]);return 1;}
unsigned L=atoi(v[2]);
if(L<1||L>MAXL){fprintf(stderr,"\nSize must be 1 to %d.\n",MAXL);return 1;}
FILE*f=fopen(v[1],"r");
if(!f){fprintf(stderr,"\nCouldn't open file.\n");return 1;}
WL=calloc(MAXW,sizeof*WL); // 2 MB
if(!WL){fprintf(stderr,"\nCouldn't allocate wordlist.\n");return 1;}
char b[MAXL*2];
while(fgets(b,sizeof b,f)){
b[strcspn(b,"\r\n")]=0;
if(strlen(b)==L){
for(char*p=b;*p;p++)*p=toupper(*p);
WL[WC++]=strdup(b);ins(b);
}
if(WC>=MAXW){fprintf(stderr,"\nWord limit reached.\n");break;}
}
fclose(f);
printf("\nLoaded %u words of length %u.\n",WC,L);
square(L);
printf("\nFound %u Sator squares of size %u.\n",found,L);
for(int i=0;i<HSIZE;++i){W*w=H[i];while(w){W*n=w->n;free(w->w);free(w);w=n;}}
for(unsigned i=0;i<WC;++i)free(WL[i]);
free(WL);
return 0;
}
No comments:
Post a Comment
Barely anyone comments, so I don't moderate. Free advertising, I guess.