#include <perms.h>

//          Edinburgh IMP77 Compiler - Version 8.4
//
//     1  ! i77 collatz.imp
//     2  ! ./collatz
//     3  ! 837799
//     4  ! ./collatz
//     5  ! 8400511
//     6
//     7  ! https://www.reddit.com/r/C_Programming/comments/1eoc0v3/does_any_c_compiler_other_than_gcc_support_nested/
//     8
//     9  ! This is an imp77 source that corresponds more or less to
//    10  ! the example C source in the accompanying ccollatz.c file.
//    11
//    12  %begin

int main(int argc, char **argv) { /*%begin*/
  _imp_initialise(argc, argv);

  //    13    %integer odd1 = 1, odd2 = 2

  int ODD1;
  ODD1 = 1;
  int ODD2;
  ODD2 = 2;

  //    14    %const %integer final value = 0, MAX FACTOR = (((~0)>>1)-1)//3
  //    15                                    ! The integer overflow test here will trigger
  //    16                                    ! at 0x7FFFFFFF rather than 0xFFFFFFFF because
  //    17                                    ! this imp77 does not support unsigned 32-bit
  //    18                                    ! integers.  Other than that case, this code
  //    19                                    ! should give the same results as the C version.
  //    20
  //    21    %const %integer terminating value = 1
  //    22
  //    23    %routine show collatz length(%integer i, %integername max)

  void SHOWCOLLATZLENGTH(int I, int *MAX) {
    //    24      ! Returns 0 for the length if there was an integer overflow.
    //    25
    //    26      %integer base value = final value + 1

    int BASEVALUE;
    BASEVALUE = 0 + 1;
    auto int ODDCOLLATZ(int I);

    //    27
    //    28      %integerfnspec odd collatz(%integer i); ! mutual recursion requires a forward declaration!
    //    29
    //    30      %integerfn even collatz(%integer i)

    int EVENCOLLATZ(int I) {
      //    31        %if i < terminating value %then %result = final value

      if (I >= 1) goto L_0001;
      return 0;
    L_0001:;

    //    32        %if i > max %then max = i

      if (I <= *MAX) goto L_0002;
      *MAX = I;
    L_0002:;

    //    33        %if i&odd2 # 0 %start ;! 'optimization' :-)

      if (I & ODD2 == 0) goto L_0003;

        //    34          %result = base value+odd collatz(i>>1); !  (really just an excuse to justify this use of mutual recursion)

      return BASEVALUE + ODDCOLLATZ((unsigned int)I >> 1);

      //    35        %finish %else %start

    L_0003:;

    //    36          %result = base value+even collatz(i>>1)

      return BASEVALUE + EVENCOLLATZ((unsigned int)I >> 1);

      //    37        %finish
      //    38      %end

    } /* end of EVENCOLLATZ */

    //    39
    //    40      %integerfn odd collatz(%integer i)

    int ODDCOLLATZ(int I) {
      //    41        %if i <= terminating value %then %result = final value

      if (I > 1) goto L_0004;
      return 0;
    L_0004:;

    //    42        %if i > max %then max = i

      if (I <= *MAX) goto L_0005;
      *MAX = I;
    L_0005:;

    //    43        %if i > MAX FACTOR %then %signal 15,1,1; ! next call would cause integer overflow...

      if (I <= 715827882) goto L_0006;
      _imp_signal(15, 1, 1, "");
    L_0006:;

    //    44        %result = base value+even collatz((i<<1)+i+1)

      return BASEVALUE + EVENCOLLATZ((I << 1) + I + 1);

      //    45      %end

    } /* end of ODDCOLLATZ */

    //    46
    //    47      print string("The Collatz sequence for")

    _imp_PRINTSTRING(_imp_str_literal("The Collatz sequence for"));

    //    48      write(i, 1)

    _imp_WRITE(I, 1);

    //    49      print string(" takes")

    _imp_PRINTSTRING(_imp_str_literal(" takes"));

    //    50
    //    51      %onevent 15 %start

    if (!_imp_on_event(15)) goto L_0007;

      //    52        print string(" an unknown number of steps (we hit an integer overflow)")

    _imp_PRINTSTRING(_imp_str_literal(" an unknown number of steps (we hit an integer overflow)"));

    //    53        newline

    _imp_NEWLINE();

    //    54        %return

    return;

    //    55      %finish

  L_0007:;

  //    56
  //    57      %if i&odd1 # 0 %then write(odd collatz(i), 1) %else write(even collatz(i), 1)

    if (I & ODD1 == 0) goto L_0008;
    _imp_WRITE(ODDCOLLATZ(I), 1);
    goto L_0009;
  L_0008:;
    _imp_WRITE(EVENCOLLATZ(I), 1);
  L_0009:;

  //    58      print string(" steps and the largest value reached was")

    _imp_PRINTSTRING(_imp_str_literal(" steps and the largest value reached was"));

    //    59      write(max, 1)

    _imp_WRITE(*MAX, 1);

    //    60      newline

    _imp_NEWLINE();

    //    61    %end

    return;
  } /* end of SHOWCOLLATZLENGTH */

  //    62
  //    63    %integer highest, seed

  int HIGHEST;
  int SEED;

  //    64
  //    65    prompt("Integer seed value: ")

  _imp_PROMPT(_imp_str_literal_size(15, "Integer seed value: "));

  //    66    read(seed)

  _imp_READ(/*?*/ &SEED);

  //    67
  //    68    %if seed < 0 %start

  if (SEED >= 0) goto L_000A;

    //    69      print string("Parameter must be a positive integer."); newline

  _imp_PRINTSTRING(_imp_str_literal("Parameter must be a positive integer."));
  _imp_NEWLINE();

  //    70      %stop

  _imp_stop(0);

  //    71    %finish

L_000A:;

//    72
//    73    highest = 0; show collatz length(seed-1, highest)

  HIGHEST = 0;
  SHOWCOLLATZLENGTH(SEED - 1, /*?*/ &HIGHEST);

  //    74    highest = 0; show collatz length(seed,   highest)

  HIGHEST = 0;
  SHOWCOLLATZLENGTH(SEED, /*?*/ &HIGHEST);

  //    75    highest = 0; show collatz length(seed+1, highest)

  HIGHEST = 0;
  SHOWCOLLATZLENGTH(SEED + 1, /*?*/ &HIGHEST);

  //    76
  //    77  %endofprogram

  _imp_stop(0);
  return 0;
end_0:;
}