target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

%FILE = type opaque
@stdout = global %FILE* null

declare void @exit(i32)
declare i32 @fputs(i8*, %FILE*)
declare %FILE* @fdopen(i32, i8*)
declare void @fclose(%FILE*)

@__BACKSLASH = internal constant [2 x i8] c"\\\00"
@__EOF = internal constant [2 x i8] c"\\\FF"
@__EOL = internal constant [2 x i8] c"\0A\00"
@__QUOTE = internal constant [2 x i8] c"\22\00"
@__FALSE = internal constant [1 x i8] c"\00"
@__TRUE = internal constant [2 x i8] c"1\00"

define i8* @__drop_i8_type(i8* %0) alwaysinline readnone
{
    ret i8* %0
}
define i8* @__eq(i8* %0, i8* %1)
{
    %i = alloca i64, align 8

    store i64 0, i64* %i
    br label %loop
loop:
    %index = load i64, i64* %i
    %char0_adr = getelementptr i8, i8* %0, i64 %index
    %char1_adr = getelementptr i8, i8* %1, i64 %index

    %char0 = load i8, i8* %char0_adr
    %char1 = load i8, i8* %char1_adr

    %chars_differ = icmp ne i8 %char0, %char1
    br i1 %chars_differ, label %lbl_not_equal, label %lbl_equal
lbl_equal:
    %string_end = icmp eq i8 %char0, 0
    br i1 %string_end, label %string_equal, label %next_char
string_equal:
    ret i8* @__TRUE
next_char:
    %next_index = add i64 %index, 1     
    store i64 %next_index, i64* %i
    br label %loop

lbl_not_equal:
    ret i8* @__FALSE
}
define i8* @__lt(i8* %0, i8* %1)
{
    %i = alloca i64, align 8

    store i64 0, i64* %i
    br label %loop
loop:
    %index = load i64, i64* %i
    %char0_adr = getelementptr i8, i8* %0, i64 %index
    %char1_adr = getelementptr i8, i8* %1, i64 %index

    %char0 = load i8, i8* %char0_adr
    %char1 = load i8, i8* %char1_adr

    %char0_eol = icmp eq i8 %char0, 0
    %char1_eol = icmp eq i8 %char1, 0

    ; if char0 == 0
    ;   if char1 == 0
    ;      return FALSE  ; "" < "" === False
    ;   else
    ;      return TRUE   ; "" < "." === True
    ;   endif
    ; else
    ;   if char1 == 0
    ;      return FALSE  ; "." < "" === False
    ;   else
    ;     if char0 < char1
    ;       return TRUE ; "a" < "b" === True
    ;     else
    ;       if char0 > char1
    ;         return FALSE ; "b" < "a" === False
    ;       else
    ;         jump next_char
    ;       endif
    ;     endif
    ;   endif
    ; endif
    br i1 %char0_eol, label %lbl_char0_eol, label %lbl_char0_not_eol
lbl_char0_eol:
    br i1 %char1_eol, label %lbl_not_smaller, label %lbl_smaller
lbl_char0_not_eol:
    br i1 %char1_eol, label %lbl_not_smaller, label %lbl_char0_not_eol_char1_not_eol
lbl_char0_not_eol_char1_not_eol:
    %char0_lt_char1 = icmp ult i8 %char0, %char1
    br i1 %char0_lt_char1, label %lbl_smaller, label %lbl_char0_ge_char1
lbl_char0_ge_char1:
    %char0_gt_char1 = icmp ugt i8 %char0, %char1
    br i1 %char0_gt_char1, label %lbl_not_smaller, label %next_char
next_char:
    %next_index = add i64 %index, 1     
    store i64 %next_index, i64* %i
    br label %loop

lbl_smaller:
    ret i8* @__TRUE
lbl_not_smaller:
    ret i8* @__FALSE
}
define i8* @__not(i8* %0)
{
    %char0_adr = getelementptr i8, i8* %0, i64 0
    %char0 = load i8, i8* %char0_adr
    %char0_eol = icmp eq i8 %char0, 0
    br i1 %char0_eol, label %lbl_empty, label %lbl_not_empty
lbl_empty:
    ret i8* @__TRUE;
lbl_not_empty:
    ret i8* @__FALSE;
}
define i8* @__add(i8* %0, i8* %1)
{
    ret i8* %0 ; STUB
}
define i8* @__emit(i8* %0)
{
    %stdout_ptr = load %FILE*, %FILE** @stdout
    call i32 @fputs(i8* %0, %FILE* %stdout_ptr)
    ret i8* @__FALSE
}
define i8* @__trace(i8* %0, i8* %1)
{
    ret i8* %0 ; STUB
}
define i8* @__peek()
{
    ret i8* @__EOL ; STUB
}
define i8* @__skip()
{
    ret i8* @__EOL ; STUB
}
define i8* @__stdinlineno()
{
    ret i8* @__EOL ; STUB
}
define i8* @__stdincolno()
{
    ret i8* @__EOL ; STUB
}
define i8* @__mapgetkey(i8* %0, i8* %1, i8* %2)
{
    ret i8* %0 ; STUB
}
define i8* @__mapsetkey(i8* %0, i8* %1, i8* %2)
{
    ret i8* %0 ; STUB
}
define i8* @__strlen(i8* %0)
{
    ret i8* %0 ; STUB
}
define i8* @__intinc(i8* %0)
{
    ret i8* %0 ; STUB
}


define void @test_result_1(i8* %func, i8* %arg0, i8* %result)
{
    call i32 @__emit(i8* %func)
    call i32 @__emit(i8* @str.bracket)
    call i32 @__emit(i8* %arg0)
    call i32 @__emit(i8* @str.bracket_colon_space)
    call i32 @__emit(i8* %result)
    call i32 @__emit(i8* @__EOL)
    ret void
}
define void @test_result_2(i8* %func, i8* %arg0, i8* %arg1, i8* %result)
{
    call i32 @__emit(i8* %func)
    call i32 @__emit(i8* @str.bracket)
    call i32 @__emit(i8* %arg0)
    call i32 @__emit(i8* @str.comma_space)
    call i32 @__emit(i8* %arg1)
    call i32 @__emit(i8* @str.bracket_colon_space)
    call i32 @__emit(i8* %result)
    call i32 @__emit(i8* @__EOL)
    ret void
}

define void @test_eq(i8* %0, i8* %1)
{
    %result = call i8* @__eq(i8* %0, i8* %1)
    call void @test_result_2(i8* @str.eq, i8* %0, i8* %1, i8* %result)
    ret void
}
define void @test_lt(i8* %0, i8* %1)
{
    %result = call i8* @__lt(i8* %0, i8* %1)
    call void @test_result_2(i8* @str.lt, i8* %0, i8* %1, i8* %result)
    ret void
}
define void @test_not(i8* %0)
{
    %result = call i8* @__not(i8* %0)
    call void @test_result_1(i8* @str.not, i8* %0, i8* %result)
    ret void
}


define i64 @main()
{
    %stdout_ptr = call %FILE* @fdopen(i32 1, i8* @str.w)
    store %FILE* %stdout_ptr, %FILE** @stdout

    call void @test_eq(i8* @str., i8* @str.0)
    call void @test_eq(i8* @str.0, i8* @str.)
    call void @test_eq(i8* @str.0, i8* @str.0)
    call void @test_eq(i8* @str.a, i8* @str.a)
    call void @test_eq(i8* @str.a, i8* @str.b)
    call void @test_eq(i8* @str.a, i8* @str.c)
    call void @test_eq(i8* @str.b, i8* @str.a)
    call void @test_eq(i8* @str.b, i8* @str.b)
    call void @test_eq(i8* @str.b, i8* @str.c)
    call void @test_eq(i8* @str.c, i8* @str.a)
    call void @test_eq(i8* @str.c, i8* @str.b)
    call void @test_eq(i8* @str.c, i8* @str.c)
    call void @test_eq(i8* @str.left, i8* @str.right)
    call void @test_eq(i8* @str.left, i8* @str.left)
    call void @test_eq(i8* @str.right, i8* @str.left)
    call void @test_eq(i8* @str.right, i8* @str.right)

    call void @test_lt(i8* @str.0, i8* @str.0)
    call void @test_lt(i8* @str.a, i8* @str.a)
    call void @test_lt(i8* @str.a, i8* @str.b)
    call void @test_lt(i8* @str.a, i8* @str.c)
    call void @test_lt(i8* @str.b, i8* @str.a)
    call void @test_lt(i8* @str.b, i8* @str.b)
    call void @test_lt(i8* @str.b, i8* @str.c)
    call void @test_lt(i8* @str.c, i8* @str.a)
    call void @test_lt(i8* @str.c, i8* @str.b)
    call void @test_lt(i8* @str.c, i8* @str.c)
    ; `a` and `b` are expected to have length 1.

    call void @test_not(i8* @str.)
    call void @test_not(i8* @str.0)
    call void @test_not(i8* @str.1)



    call i32 @fclose(%FILE* %stdout_ptr)

    ret i64 0
}

@str. = internal constant [1 x i8] c"\00"
@str.0 = internal constant [2 x i8] c"0\00"
@str.1 = internal constant [2 x i8] c"1\00"
@str.a = internal constant [2 x i8] c"a\00"
@str.b = internal constant [2 x i8] c"b\00"
@str.c = internal constant [2 x i8] c"c\00"
@str.bracket = internal constant [2 x i8] c"(\00"
@str.bracket_colon_space = internal constant [4 x i8] c"): \00"
@str.comma_space = internal constant [3 x i8] c", \00"
@str.eq = internal constant [3 x i8] c"eq\00"
@str.left = internal constant [5 x i8] c"left\00"
@str.lt = internal constant [3 x i8] c"lt\00"
@str.not = internal constant [4 x i8] c"not\00"
@str.right = internal constant [6 x i8] c"right\00"
@str.w = internal constant [2 x i8] c"w\00"
