// #target: macos-x64
module fasta;

const IM = 139968u;
const IA = 3877u;
const IC = 29573u;
const SEED = 42u;

uint seed = SEED;

fn float fasta_rand(float maxval)
{
	seed = (seed * IA + IC) % IM;
	return maxval * seed / IM;
}

char[] alu @private =
  "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG"
  "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA"
  "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT"
  "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA"
  "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG"
  "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC"
  "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA";

extern fn int atoi(char *s);
extern fn int printf(char *s, ...);
extern fn void putchar(int c);

char[] iub = "acgtBDHKMNRSVWY";
double[] iub_p = {
  0.27,
  0.12,
  0.12,
  0.27,
  0.02,
  0.02,
  0.02,
  0.02,
  0.02,
  0.02,
  0.02,
  0.02,
  0.02,
  0.02,
  0.02 };

char[] homosapiens = "acgt";
double[] homosapiens_p = {
  0.3029549426680,
  0.1979883004921,
  0.1975473066391,
  0.3015094502008
};

const LINELEN = 60;

// slowest character-at-a-time output
fn void repeat_fasta(char[] seq, int n)
{
	usz len = seq.len;
	int i @noinit;
	for (i = 0; i < n; i++)
	{
		putchar(seq[i % len]);
		if (i % LINELEN == LINELEN - 1) putchar('\n');
	}
	if (i % LINELEN != 0) putchar('\n');
}

fn void random_fasta(char[] symb, double[] probability, int n)
{
	assert(symb.len == probability.len);
	int len = probability.len;
	int i @noinit;
	for (i = 0; i < n; i++)
	{
		double v = fasta_rand(1.0);
		/* slowest idiomatic linear lookup.  Fast if len is short though. */
		int j @noinit;
		for (j = 0; j < len - 1; j++)
		{
			v -= probability[j];
			if (v < 0) break;
		}
		putchar(symb[j]);
		if (i % LINELEN == LINELEN - 1) putchar('\n');
	}
	if (i % LINELEN != 0) putchar('\n');
}

fn int main(int argc, char **argv)
{
	int n = 1000;
	if (argc > 1) n = atoi(argv[1]);

	printf(">ONE Homo sapiens alu\n");
	repeat_fasta(alu, n * 2);

	printf(">TWO IUB ambiguity codes\n");
	random_fasta(iub, iub_p, n * 3);

	printf(">THREE Homo sapiens frequency\n");
	random_fasta(homosapiens, homosapiens_p, n * 5);

	return 0;
}

/* #expect: fasta.ll

%"char[]" = type { ptr, i64 }
%"double[]" = type { ptr, i64 }
@fasta.IM = local_unnamed_addr constant i32 139968, align 4
@fasta.IA = local_unnamed_addr constant i32 3877, align 4
@fasta.IC = local_unnamed_addr constant i32 29573, align 4
@fasta.SEED = local_unnamed_addr constant i32 42, align 4
@fasta.seed = local_unnamed_addr global i32 42, align 4
@.str = private unnamed_addr constant [288 x i8] c"GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\00", align 1
@fasta.alu = internal unnamed_addr global %"char[]" { ptr @.str, i64 287 }, align 8
@.str.11 = private unnamed_addr constant [16 x i8] c"acgtBDHKMNRSVWY\00", align 1
@fasta.iub = local_unnamed_addr global %"char[]" { ptr @.str.11, i64 15 }, align 8
@.__const_slice = private unnamed_addr global [15 x double] [double 2.700000e-01, double 1.200000e-01, double 1.200000e-01, double 2.700000e-01, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02, double 2.000000e-02], align 16
@fasta.iub_p = local_unnamed_addr global %"double[]" { ptr @.__const_slice, i64 15 }, align 8
@.str.12 = private unnamed_addr constant [5 x i8] c"acgt\00", align 1
@fasta.homosapiens = local_unnamed_addr global %"char[]" { ptr @.str.12, i64 4 }, align 8
@.__const_slice.13 = private unnamed_addr global [4 x double] [double 0x3FD3639D20BAEB5B, double 0x3FC957AE3DCD561B, double 0x3FC9493AEAB6C2BF, double 0x3FD34BEE4B030838], align 16
@fasta.homosapiens_p = local_unnamed_addr global %"double[]" { ptr @.__const_slice.13, i64 4 }, align 8
@fasta.LINELEN = local_unnamed_addr constant i32 60, align 4
@.str.14 = private unnamed_addr constant [23 x i8] c">ONE Homo sapiens alu\0A\00", align 1
@.str.15 = private unnamed_addr constant [26 x i8] c">TWO IUB ambiguity codes\0A\00", align 1
@.str.16 = private unnamed_addr constant [31 x i8] c">THREE Homo sapiens frequency\0A\00", align 1

; Function Attrs:
define float @fasta.fasta_rand(float %0) #0 {
entry:
  %1 = load i32, ptr @fasta.seed, align 4
  %mul = mul i32 %1, 3877
  %add = add i32 %mul, 29573
  %umod = urem i32 %add, 139968
  store i32 %umod, ptr @fasta.seed, align 4
  %2 = load i32, ptr @fasta.seed, align 4
  %uifp = uitofp i32 %2 to float
  %fmul = fmul float %0, %uifp
  %fdiv = fdiv float %fmul, 1.399680e+05
  ret float %fdiv
}

; Function Attrs:
declare i32 @atoi(ptr) #0

; Function Attrs:
declare i32 @printf(ptr, ...) #0

; Function Attrs:
declare void @putchar(i32) #0

; Function Attrs:
define void @fasta.repeat_fasta(ptr %0, i64 %1, i32 %2) #0 {
entry:
  %seq = alloca %"char[]", align 8
  %len = alloca i64, align 8
  %i = alloca i32, align 4
  store ptr %0, ptr %seq, align 8
  %ptradd = getelementptr inbounds i8, ptr %seq, i64 8
  store i64 %1, ptr %ptradd, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %seq, i64 8
  %3 = load i64, ptr %ptradd1, align 8
  store i64 %3, ptr %len, align 8
  store i32 0, ptr %i, align 4
  br label %loop.cond
loop.cond:                                        ; preds = %if.exit, %entry
  %4 = load i32, ptr %i, align 4
  %lt = icmp slt i32 %4, %2
  br i1 %lt, label %loop.body, label %loop.exit
loop.body:                                        ; preds = %loop.cond
  %5 = load ptr, ptr %seq, align 8
  %6 = load i32, ptr %i, align 4
  %sext = sext i32 %6 to i64
  %7 = load i64, ptr %len, align 8
  %smod = srem i64 %sext, %7
  %ptradd2 = getelementptr inbounds i8, ptr %5, i64 %smod
  %8 = load i8, ptr %ptradd2, align 1
  %zext = zext i8 %8 to i32
  call void @putchar(i32 %zext)
  %9 = load i32, ptr %i, align 4
  %smod3 = srem i32 %9, 60
  %eq = icmp eq i32 %smod3, 59
  br i1 %eq, label %if.then, label %if.exit
if.then:                                          ; preds = %loop.body
  call void @putchar(i32 10)
  br label %if.exit
if.exit:                                          ; preds = %if.then, %loop.body
  %10 = load i32, ptr %i, align 4
  %add = add i32 %10, 1
  store i32 %add, ptr %i, align 4
  br label %loop.cond
loop.exit:                                        ; preds = %loop.cond
  %11 = load i32, ptr %i, align 4
  %smod4 = srem i32 %11, 60
  %neq = icmp ne i32 %smod4, 0
  br i1 %neq, label %if.then5, label %if.exit6
if.then5:                                         ; preds = %loop.exit
  call void @putchar(i32 10)
  br label %if.exit6
if.exit6:                                         ; preds = %if.then5, %loop.exit
  ret void
}

; Function Attrs:
define void @fasta.random_fasta(ptr %0, i64 %1, ptr %2, i64 %3, i32 %4) #0 {
entry:
  %symb = alloca %"char[]", align 8
  %probability = alloca %"double[]", align 8
  %len = alloca i32, align 4
  %i = alloca i32, align 4
  %v = alloca double, align 8
  %j = alloca i32, align 4
  store ptr %0, ptr %symb, align 8
  %ptradd = getelementptr inbounds i8, ptr %symb, i64 8
  store i64 %1, ptr %ptradd, align 8
  store ptr %2, ptr %probability, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %probability, i64 8
  store i64 %3, ptr %ptradd1, align 8
  %ptradd2 = getelementptr inbounds i8, ptr %symb, i64 8
  %5 = load i64, ptr %ptradd2, align 8
  %ptradd3 = getelementptr inbounds i8, ptr %probability, i64 8
  %6 = load i64, ptr %ptradd3, align 8
  %eq = icmp eq i64 %5, %6
  call void @llvm.assume(i1 %eq)
  %ptradd4 = getelementptr inbounds i8, ptr %probability, i64 8
  %7 = load i64, ptr %ptradd4, align 8
  %trunc = trunc i64 %7 to i32
  store i32 %trunc, ptr %len, align 4
  store i32 0, ptr %i, align 4
  br label %loop.cond
loop.cond:                                        ; preds = %if.exit13, %entry
  %8 = load i32, ptr %i, align 4
  %lt = icmp slt i32 %8, %4
  br i1 %lt, label %loop.body, label %loop.exit15
loop.body:                                        ; preds = %loop.cond
  %9 = call float @fasta.fasta_rand(float 1.000000e+00)
  %fpfpext = fpext float %9 to double
  store double %fpfpext, ptr %v, align 8
  store i32 0, ptr %j, align 4
  br label %loop.cond5
loop.cond5:                                       ; preds = %if.exit, %loop.body
  %10 = load i32, ptr %j, align 4
  %11 = load i32, ptr %len, align 4
  %sub = sub i32 %11, 1
  %lt6 = icmp slt i32 %10, %sub
  br i1 %lt6, label %loop.body7, label %loop.exit
loop.body7:                                       ; preds = %loop.cond5
  %12 = load double, ptr %v, align 8
  %13 = load ptr, ptr %probability, align 8
  %14 = load i32, ptr %j, align 4
  %sext = sext i32 %14 to i64
  %ptroffset = getelementptr inbounds [8 x i8], ptr %13, i64 %sext
  %15 = load double, ptr %ptroffset, align 8
  %fsub = fsub double %12, %15
  store double %fsub, ptr %v, align 8
  %16 = load double, ptr %v, align 8
  %lt8 = fcmp olt double %16, 0.000000e+00
  br i1 %lt8, label %if.then, label %if.exit
if.then:                                          ; preds = %loop.body7
  br label %loop.exit
if.exit:                                          ; preds = %loop.body7
  %17 = load i32, ptr %j, align 4
  %add = add i32 %17, 1
  store i32 %add, ptr %j, align 4
  br label %loop.cond5
loop.exit:                                        ; preds = %if.then, %loop.cond5
  %18 = load ptr, ptr %symb, align 8
  %19 = load i32, ptr %j, align 4
  %sext9 = sext i32 %19 to i64
  %ptradd10 = getelementptr inbounds i8, ptr %18, i64 %sext9
  %20 = load i8, ptr %ptradd10, align 1
  %zext = zext i8 %20 to i32
  call void @putchar(i32 %zext)
  %21 = load i32, ptr %i, align 4
  %smod = srem i32 %21, 60
  %eq11 = icmp eq i32 %smod, 59
  br i1 %eq11, label %if.then12, label %if.exit13
if.then12:                                        ; preds = %loop.exit
  call void @putchar(i32 10)
  br label %if.exit13
if.exit13:                                        ; preds = %if.then12, %loop.exit
  %22 = load i32, ptr %i, align 4
  %add14 = add i32 %22, 1
  store i32 %add14, ptr %i, align 4
  br label %loop.cond
loop.exit15:                                      ; preds = %loop.cond
  %23 = load i32, ptr %i, align 4
  %smod16 = srem i32 %23, 60
  %neq = icmp ne i32 %smod16, 0
  br i1 %neq, label %if.then17, label %if.exit18
if.then17:                                        ; preds = %loop.exit15
  call void @putchar(i32 10)
  br label %if.exit18
if.exit18:                                        ; preds = %if.then17, %loop.exit15
  ret void
}

; Function Attrs:
define i32 @main(i32 %0, ptr %1) #0 {
entry:
  %n = alloca i32, align 4
  store i32 1000, ptr %n, align 4
  %gt = icmp sgt i32 %0, 1
  br i1 %gt, label %if.then, label %if.exit
if.then:                                          ; preds = %entry
  %ptradd = getelementptr inbounds i8, ptr %1, i64 8
  %2 = load ptr, ptr %ptradd, align 8
  %3 = call i32 @atoi(ptr %2)
  store i32 %3, ptr %n, align 4
  br label %if.exit
if.exit:                                          ; preds = %if.then, %entry
  %4 = call i32 (ptr, ...) @printf(ptr @.str.14)
  %5 = load i32, ptr %n, align 4
  %mul = mul i32 %5, 2
  %lo = load ptr, ptr @fasta.alu, align 8
  %hi = load i64, ptr getelementptr inbounds (i8, ptr @fasta.alu, i64 8), align 8
  call void @fasta.repeat_fasta(ptr %lo, i64 %hi, i32 %mul)
  %6 = call i32 (ptr, ...) @printf(ptr @.str.15)
  %7 = load i32, ptr %n, align 4
  %mul1 = mul i32 %7, 3
  %lo2 = load ptr, ptr @fasta.iub, align 8
  %hi3 = load i64, ptr getelementptr inbounds (i8, ptr @fasta.iub, i64 8), align 8
  %lo4 = load ptr, ptr @fasta.iub_p, align 8
  %hi5 = load i64, ptr getelementptr inbounds (i8, ptr @fasta.iub_p, i64 8), align 8
  call void @fasta.random_fasta(ptr %lo2, i64 %hi3, ptr %lo4, i64 %hi5, i32 %mul1)
  %8 = call i32 (ptr, ...) @printf(ptr @.str.16)
  %9 = load i32, ptr %n, align 4
  %mul6 = mul i32 %9, 5
  %lo7 = load ptr, ptr @fasta.homosapiens, align 8
  %hi8 = load i64, ptr getelementptr inbounds (i8, ptr @fasta.homosapiens, i64 8), align 8
  %lo9 = load ptr, ptr @fasta.homosapiens_p, align 8
  %hi10 = load i64, ptr getelementptr inbounds (i8, ptr @fasta.homosapiens_p, i64 8), align 8
  call void @fasta.random_fasta(ptr %lo7, i64 %hi8, ptr %lo9, i64 %hi10, i32 %mul6)
  ret i32 0
}