#ifndef __VISIBLE_H__
#define __VISIBLE_H__ 1

// TO DO: some string sizes such as for TYPENAME and JDM's INT2 functions.  tidy use of 31 vs 32.

#if defined(__GNUC__) || defined(__clang__)
#define _imp_INFINITY (__builtin_inff())
#elif defined(_MSC_VER)
#define _imp_INFINITY ((float)(1e+300 * 1e+300))
#else
#define _imp_INFINITY 0x1.0p128f
#endif

#include "imptypes.h"  // Note: type _imp_name undergoing work
#include "externalfile.h"
#include "internal/internal.h"

// The perms that IMP code calls are implemented by 'static inline' calls on code in internal.[hc]

#ifdef NOT_AT_THE_MOMENT
static inline int _imp_NL(void) {
  return '\n';
}
#endif

static inline double _imp_PI(void) {
  return 3.141592653589793238462;
}

static inline _imp_string1 _imp_SNL(void) {
  _imp_string1 SNL = {.charno = {1, '\n'} };
  return SNL;
}

#define sign(v) ({ __typeof__(v) _v = (v); (_v > 0) - (_v < 0); })

// A bithack solution that *may* optimise the tests for invalid for-loops
// while paying heed to different compiler optimisation levels.

// initial, final, and increment must be frozen copies of initial values or literal constants...
#define invalid_for_loop(initial, increment, final) ({ int _range = (final) - (initial); \
  (                                                                                                                 \
     (_range ^ (increment)) < 0)                                                                                    \
   ||                                                                                                               \
     (__builtin_constant_p(increment) ? (((increment) == 1 || (increment) == -1) ? 0 : (_range % (increment) != 0)) \
                                      : (((increment) != 1 && (increment) != -1) &&    (_range % (increment) != 0)) \
  );                                                                                                                \
})

// #########################################################################################

// forward references:

static inline _imp_string1 _imp_TOSTRING(int C);
static inline unsigned char *_imp_CHARNO(_imp_string *str, int i);
static inline unsigned char *_imp_LENGTH(_imp_string *str);

static inline double _imp_FLOAT(double N); // might be wrong. might be for converting an int to a real.

static inline _imp_address _imp_ADDR(void *P);

static inline unsigned char *_imp_BYTE(_imp_address N);
static inline unsigned char *_imp_BYTEINTEGER(_imp_address N);

static inline short *_imp_SHORT(_imp_address N);
static inline short *_imp_SHORTINTEGER(_imp_address N);

static inline int *_imp_INTEGER(_imp_address N);

static inline long long int *_imp_LONG(_imp_address N);
static inline long long int *_imp_LONGINTEGER(_imp_address N);

//static inline long long *_imp_LONGLONG(_imp_address N);
//static inline long long *_imp_LONGLONGINTEGER(_imp_address N);

static inline long long int _imp_LENGTHENI(int I);
static inline int _imp_SHORTENI(long long int I);

static inline float *_imp_REAL(_imp_address N);

static inline double *_imp_LONGREAL(_imp_address N);

static inline _imp_string *_imp_STRING(_imp_address N);

static inline void *_imp_RECORD(_imp_address N);

// Note: the VMS compiler only implemented a subset of types and the
// coding appears somewhat adhoc: /home/imp2023/VMS/imp77-vax/lib/namerts.imp

// These typeof codes will have to be kept in synch with perms.inc, where
// we find this note taken from https://gtoal.com/imp77/reference-manual/Imp77_Core_Environment/core11.html

//! This function returns a code which indicates the type of the
//! object supplied as parameter.  The complete list of code values
//! which may be returned by TYPE OF is as follows:
//!
//! 0 - unknown type
//! 1 - integer
//! 2 - real
//! 3 - string
//! 4 - record
//! 5 - byte integer
//! 6 - short integer
//! 7 - long integer
//! 8 - long real
//! 9 - array
//! 10 - label
//!
//! Note that an actual runtime call will be necessary in the case of
//! %name parameters, but for known variables, the type information can
//! be supplied by the compiler directly.  So, a prim/perm hybrid.

// Note: handling %label in C would prove very difficult.  However the sizeof/typeof calls
// can be handled in the compiler itself for known variables as an optimisation, so should
// handle %label this way even if we don't optimise other cases.  However %name variables
// and parameters are another question, but (pending conformation by testing) I don't believe
// it is possible to store a %label in a %name, whether a %name variable or a %name parameter

#define _imp_type_UNKNOWN      0

#define _imp_type_INTEGER      1
#define _imp_type_REAL         2
#define _imp_type_STRING       3
#define _imp_type_RECORD       4
#define _imp_type_BYTEINTEGER  5
#define _imp_type_SHORTINTEGER 6
#define _imp_type_LONGINTEGER  7
#define _imp_type_LONGREAL     8

#define _imp_type_ARRAY        9   // NOT YET TESTED
#define _imp_type_LABEL        10  // NOT GOING TO BE IMPLEMENTED

#define _imp_type_NAME         16

#define _imp_type_INTEGERNAME      (_imp_type_INTEGER+16)
#define _imp_type_REALNAME         (_imp_type_REAL+16)
#define _imp_type_STRINGNAME       (_imp_type_STRING+16)
#define _imp_type_RECORDNAME       (_imp_type_RECORD+16)
#define _imp_type_BYTEINTEGERNAME  (_imp_type_BYTEINTEGER+16)
#define _imp_type_SHORTINTEGERNAME (_imp_type_SHORTINTEGER+16)
#define _imp_type_LONGINTEGERNAME  (_imp_type_LONGINTEGER+16)
#define _imp_type_LONGREALNAME     (_imp_type_LONGREAL+16)


// I suspect we really want to return the type of the variable that a %NAME points to.
// which means looking inside the private _imp_name struct that is used to contain the
// %name info, using a runtime function such as:

// extern int                      _imp_typeof(_imp_name *N);
// extern int                      _imp_sizeof(_imp_name *N);

// NOTE THAT THE *NAME TYPES BELOW WILL PROBABLY NOT WORK.
#define _imp_TYPEOF(var)                                                                      \
   (__builtin_types_compatible_p(__typeof__(*var), unsigned char) ?    _imp_type_BYTEINTEGER :      \
     (__builtin_types_compatible_p(__typeof__(*var), short int) ?        _imp_type_SHORTINTEGER :     \
       (__builtin_types_compatible_p(__typeof__(*var), int) ?              _imp_type_INTEGER :          \
         (__builtin_types_compatible_p(__typeof__(*var), long long int) ?    _imp_type_LONGINTEGER :      \
           (__builtin_types_compatible_p(__typeof__(*var), float) ?            _imp_type_REAL :             \
             (__builtin_types_compatible_p(__typeof__(*var), double) ?           _imp_type_LONGREAL :         \
               (__builtin_types_compatible_p(__typeof__(*var), _imp_string) ?      _imp_type_STRING :           \
                 (__builtin_types_compatible_p(__typeof__(*var), void) ?             _imp_type_NAME :             \
                   (__builtin_types_compatible_p(__typeof__(*var), unsigned char *) ?  _imp_type_BYTEINTEGERNAME :  \
                     (__builtin_types_compatible_p(__typeof__(*var), short int *) ?      _imp_type_SHORTINTEGERNAME : \
                       (__builtin_types_compatible_p(__typeof__(*var), int *) ?            _imp_type_INTEGERNAME :      \
                         (__builtin_types_compatible_p(__typeof__(*var), long long int *) ?  _imp_type_LONGINTEGERNAME :  \
                           (__builtin_types_compatible_p(__typeof__(*var), float *) ?          _imp_type_REALNAME :         \
                             (__builtin_types_compatible_p(__typeof__(*var), double *) ?         _imp_type_LONGREALNAME :     \
                               (__builtin_types_compatible_p(__typeof__(*var), _imp_string *) ?    _imp_type_STRINGNAME :       \
                                 (__builtin_types_compatible_p(__typeof__(*var), _imp_name *) ?      _imp_type_NAME :             \
                                   (__builtin_types_compatible_p(__typeof__(*var), void *) ?           _imp_type_RECORDNAME :       \
                                     sizeof(*var) > 8 ? _imp_type_RECORD : _imp_type_UNKNOWN                                      \
                                   ) \
                                 ) \
                               ) \
                             ) \
                           ) \
                         ) \
                       ) \
                     ) \
                   ) \
                 ) \
               ) \
             ) \
           ) \
         ) \
       ) \
     ) \
   )

#define _imp_SIZEOF(var)                                                                                                 \
 (__builtin_types_compatible_p(__typeof__(*var), unsigned char)  ?   sizeof(char) :                                          \
   (__builtin_types_compatible_p(__typeof__(*var), short int)  ?       sizeof(short) :                                       \
     (__builtin_types_compatible_p(__typeof__(*var), int)  ?             sizeof(int) :                                       \
       (__builtin_types_compatible_p(__typeof__(*var), long long int)  ?   sizeof(long long int) :                           \
         (__builtin_types_compatible_p(__typeof__(*var), float)  ?           sizeof(float) :                                 \
           (__builtin_types_compatible_p(__typeof__(*var), double)  ?          sizeof(double) :                              \
             (__builtin_types_compatible_p(__typeof__(*var), unsigned char *)  ? sizeof(void *) :                            \
               (__builtin_types_compatible_p(__typeof__(*var), short int *)  ?     sizeof(void *) :                          \
                 (__builtin_types_compatible_p(__typeof__(*var), int *)  ?           sizeof(void *) :                        \
                   (__builtin_types_compatible_p(__typeof__(*var), long long int *)  ? sizeof(void *) :                      \
                     (__builtin_types_compatible_p(__typeof__(*var), float *)  ?         sizeof(void *) :                    \
                       (__builtin_types_compatible_p(__typeof__(*var), double *)  ?        sizeof(void *) :                  \
                         (__builtin_types_compatible_p(__typeof__(*var), _imp_string *)  ?   sizeof(void *) :                \
                           sizeof(*var)                                                                                  \
                         ) \
                       ) \
                     ) \
                   ) \
                 ) \
               ) \
             ) \
           ) \
         ) \
       ) \
     ) \
   ) \
 )

//                             (__builtin_types_compatible_p(__typeof__(*var), _imp_name)  ?        _imp_sizeof(var) : \ ...
//                               (__builtin_types_compatible_p(__typeof__(*var), _imp_name *)  ?      _imp_sizeof(*var)        \ ...
//                               ) \ ...
//                             ) \ ...

extern int _imp_Control;   // %control  0xnnnnnn and command-line option flags.
static inline void __attribute__((always_inline)) _imp_control(int bits) {
  _imp_Control = bits;
}
#define _IMP_CONTROL_TRACE 1024

extern int _imp_Diagnose;  // %diagnose 0xnnnnnn and command-line option flags.
static inline void __attribute__((always_inline)) _imp_diagnose(int bits) {
  _imp_Diagnose = bits;
}
#define _IMP_DIAG_SIGNALS 1024

static inline double                   _imp_ARCSIN(double ANGLE);
static inline double                   _imp_ARCCOS(double ANGLE);
static inline double                   _imp_ARCTAN(double X, double Y);
static inline double                   _imp_ARCTAN1(double ANGLE);
static inline double                   _imp_SIN(double ANGLE);
static inline double                   _imp_COS(double ANGLE);
static inline double                   _imp_TAN(double ANGLE);
static inline double                   _imp_COT(double ANGLE);
static inline double                   _imp_FRACTION(double R);
static inline double                   _imp_FRACPT(double R);
static inline int                      _imp_IMOD(int I);

#ifdef NEVER

// This is why perms need to be output in C using private internal names such as _imp_imod(N) ...

// %integerfn IMOD(%integer n)
//  %result = |N|
// %end

// If a user program did the above, it would cause an infinite recusion!

#endif

/* Perms from the earliest Imp compiler (allimpc1):
SELECTINPUT SELECTOUTPUT NEWLINE SPACE SKIPSYMBOL RUNOUT NEWLINES
SPACES NEXTSYMBOL PRINTSYMBOL READSYMBOL READ WRITE NEWPAGE ADDR
ARCSIN INT INTPT FRACPT PRINT PRINTFL REAL INTEGER MOD ARCCOS SQRT
LOG SIN COS TAN EXP MAPRECORD BYTEINTEGER SHORTINTEGER RADIUS
ARCTAN PARITY PRINTSTRING NL LONGREAL PRINTCH READCH STRING

Unimplemented ones are:
runout (paper tape, can ignore!)
maprecord (probably old name for RECORD())
radius (sqrt(a^2+b^2)
parity

So... *might* want to implement RADIUS() and PARITY()???

A relatively portable library where we can get non-machine-dependent real handling
can be found in https://gtoal.com/imp77/reference-manual/Library_sources/apm_lib/
- could be the one to use for print/printfl/rtos ..?

*/

static inline double                   _imp_MOD(double R);
static inline int                      _imp_INT(double R);
static inline int                      _imp_INTPT(double R);
static inline long int                 _imp_LINT(double R);
static inline long int                 _imp_LINTPT(double R);
static inline int                      _imp_TRUNC(double R);
static inline int                      _imp_ROUND(double R);
static inline long long int            _imp_IEXP(int NUM, int POWER);
static inline double                   _imp_REXP(double NUM, double POWER);
static inline double                   _imp_EXP(double NUM);
static inline double                   _imp_LOG(double X);
static inline int                      _imp_REM(int P, int Q);
static inline double                   _imp_SQRT(double NUM);
static inline double                   _imp_RADIUS(double X, double Y);
static inline int                      _imp_ISQRT(int NUM);
static inline int                      _imp_MULDIV(int A, int B, int C);
static inline void                     _imp_PROMPT(_imp_string15 S);
static inline void                     _imp_READSYMBOL(int *P);
static inline void                     _imp_READCH(int *P);
static inline int                      _imp_NEXTSYMBOL(void);
static inline int                      _imp_NEXTCH(void);
static inline void                     _imp_SKIPSYMBOL(void);
static inline void                     _imp_PRINTSYMBOL(char SYM);
static inline void                     _imp_PRINTCH(char SYM);
static inline void                     _imp_PRINTSTRING(_imp_string S);

// experimenting with different ways to implement longinteger:
//static inline void                     _imp_WRITE(int V, int P);

  // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}

static inline void                     _imp_READITEM(_imp_string *S);
static inline void                     _imp_READSTRING(_imp_string *S);
static inline void                     _imp_READTEXT(_imp_string *S, int DELIM);
static inline _imp_string1             _imp_NEXTITEM(void);
static inline void                     _imp_READLINE(_imp_string *S);
static inline int                      _imp_INSTREAM(void);
static inline int                      _imp_OUTSTREAM(void);
static inline int                      _imp_INPUTSTREAM(void);
static inline int                      _imp_OUTPUTSTREAM(void);
static inline _imp_string              _imp_INPUTNAME(void);
static inline _imp_string              _imp_OUTPUTNAME(void);
static inline _imp_string              _imp_INFILENAME(void);
static inline _imp_string              _imp_OUTFILENAME(void);
static inline void                     _imp_SELECTINPUT(int N);
static inline void                     _imp_SELECTOUTPUT(int N);
static inline void                     _imp_OPENINPUT(int N, _imp_string FD);
static inline void                     _imp_OPENOUTPUT(int N, _imp_string FD);
static inline void                     _imp_OPENBINARYINPUT(int N, _imp_string FD);
static inline void                     _imp_OPENBINARYOUTPUT(int N, _imp_string FD);
static inline void                     _imp_DEFINEINPUT(int I, _imp_string SPEC);
static inline void                     _imp_DEFINEOUTPUT(int I, _imp_string SPEC);
static inline void                     _imp_ABANDONINPUT(void);
static inline void                     _imp_ABANDONOUTPUT(void);
static inline void                     _imp_CLOSESTREAM(int stream);
static inline void                     _imp_CLOSEINPUT(void);
static inline void                     _imp_CLOSEOUTPUT(void);
static inline void                     _imp_RESETINPUT(void);
static inline void                     _imp_RESETOUTPUT(void);
static inline void                     _imp_COMPLETEINPUT(void);
static inline void                     _imp_COMPLETEOUTPUT(void);
static inline void                     _imp_POSITIONINPUT(int P);
static inline void                     _imp_POSITIONOUTPUT(int P);
static inline void                     _imp_SETINPUT(int P);
static inline void                     _imp_SETOUTPUT(int P);
static inline void                     _imp_INPUTPOSITION(void);
static inline void                     _imp_OUTPUTPOSITION(void);
static inline int                      _imp_ENDOFINPUT(void);
static inline int                      _imp_INPOS(void);
static inline int                      _imp_OUTPOS(void);
static inline void                     _imp_SPACE(void);
static inline void                     _imp_SPACES(int N);
static inline void                     _imp_NEWPAGE(void);
static inline void                     _imp_NEWLINE(void);
static inline void                     _imp_NEWLINES(int N);
static inline void                     _imp_PRINT(double R, int BEFORE, int AFTER);        // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}
static inline void                     _imp_PRINTFLOATING(double R, int A, int B);
static inline void                     _imp_PRINTFL(double R, int PLACES);
static inline void                     _imp_PRINTFHEX(double R);
static inline _imp_string              _imp_SUBSTRING(_imp_string *S, int FROM, int TO);
static inline _imp_string              _imp_FROMSTRING(_imp_string *S, int FROM, int TO);
static inline _imp_string              _imp_TRIM(_imp_string S, int MAX);
static inline _imp_string8             _imp_TIME(void);
static inline _imp_string8             _imp_DATE(void);
#ifdef WANT_EMAS_STYLE
static inline float                    _imp_CPUTIME(void);  // Result is seconds
#else
static inline int                      _imp_CPUTIME(void);  // Result is milliseconds
#endif
static inline _imp_string              _imp_CLIPARAM(void);

// EVENT calls all in psignal.h now

static inline _imp_string              _imp_ITOS(int I, int P);
static inline int                      _imp_STOI(_imp_string S);
static inline _imp_string              _imp_SSFMESSAGE(void);

static inline void                     _imp_stop(int STATUS); // generated by %stop


// #########################################################################################

static inline int __attribute__((always_inline)) _imp_RANDOMINTEGER(void) {
  // %perm %integer %fn %spec random integer  ; ! CS library
  extern int _imp_randominteger(void);

  return _imp_randominteger();
}

static inline float __attribute__((always_inline)) _imp_RANDOMREAL(void) {
  // %perm %real %fn %spec random real        ; ! invented:
  extern float _imp_randomreal(void);

  return _imp_randomreal();
}

static inline double __attribute__((always_inline)) _imp_RANDOMLONGREAL(void) {
  // %perm %long %real %fn %spec random long real
  extern double _imp_randomlongreal(void);

  return _imp_randomlongreal();
}

static inline void __attribute__((always_inline)) _imp_JUGGLERANDOMS(void) {
  // %external %routine %spec juggle randoms  ; ! PDS CS1 library (misreported once somewhere as 'fiddle my randoms')
  extern void _imp_jugglerandoms(void);

  _imp_jugglerandoms();
}

// #########################################################################################

// Actual procedures:

static inline int __attribute__((always_inline)) _imp_ARGC(void) {
  return _imp_global_argc;
}

static inline _imp_string __attribute__((always_inline)) _imp_ARGV(int n) {
  return _imp_c2istr(_imp_global_argv[n]);
}

static inline _imp_string1 __attribute__((always_inline)) _imp_TOSTRING(int C) {
  _imp_string1 tmp;
  *_imp_LENGTH((_imp_string *)&tmp) = 1; *_imp_CHARNO((_imp_string *)&tmp, 1) = (C&255);
  return tmp; // Return whole string on the stack, not just a pointer.
}

static inline unsigned char * __attribute__((always_inline)) _imp_CHARNO(_imp_string *str, int i) {
  // TO DO: check 0 <= i <= LENGTH(str)  (worry about sizeof str later)
  return &str->charno[i];
}

static inline unsigned char * __attribute__((always_inline)) _imp_LENGTH(_imp_string *str) { return &str->length; }

static inline double __attribute__((always_inline)) _imp_FLOAT(double N) {
  return N; // if FLOAT() is passed an integer, C will promote it to double.
            // %real in imp is evaluated as double everywhere except when
            // assigned to a 4-byte memory location.
}

static inline _imp_address __attribute__((always_inline)) _imp_ADDR(void *P) {
  return (_imp_address)P;
}

//%record(*)%map NEW(%name v)  *** This is a tricky one and may need some work.
//#define _imp_NEW(var) ((__typeof__(var))malloc(sizeof(*(var))))
//#define _imp_NEW(var) malloc(_imp_SIZEOF(var))
#define _imp_NEW(var) c_malloc(_imp_SIZEOF(var))
static inline void * __attribute__((always_inline)) c_malloc(int size) {
  extern void *malloc(size_t size);
  return malloc(size);
}

//%routine DISPOSE(%name v)
//#define _imp_DISPOSE(var) free(var)
static inline void __attribute__((always_inline)) _imp_DISPOSE(void *ptr) {
  extern void free(void *ptr);
  free(ptr);
}

// 8 bit
static inline unsigned char * __attribute__((always_inline)) _imp_BYTE(_imp_address N) {
  return (unsigned char *)N;
}

// 8 bit
static inline unsigned char * __attribute__((always_inline)) _imp_BYTEINTEGER(_imp_address N) {
  return (unsigned char *)N;
}

// 16 bit
static inline short int * __attribute__((always_inline)) _imp_SHORT(_imp_address N) {
  return (short int *)N;
}

// 16 bit
static inline short int * __attribute__((always_inline)) _imp_SHORTINTEGER(_imp_address N) {
  return (short int *)N;
}

// 32 bit
static inline int * __attribute__((always_inline)) _imp_INTEGER(_imp_address N) {
  // really a C 'long int' but a lot of hassle to change this everywhere
  //
  return (int *)N;
}

// 64 bit
static inline long long int * __attribute__((always_inline)) _imp_LONG(_imp_address N) {
  return (long long int *)N;
}

// 64 bit
static inline long long int * __attribute__((always_inline)) _imp_LONGINTEGER(_imp_address N) {
  return (long long int *)N;
}

// 128 bit Doesn't exist in Imp77 yet.
//static inline long long *_imp_LONGLONG(_imp_address N) {
//  return (long long *)N;
//}

// 128 bit Doesn't exist in Imp77 yet.
//static inline long long *_imp_LONGLONGINTEGER(_imp_address N) {
//  return (long long *)N;
//}

// 64 bit
static inline long long int __attribute__((always_inline)) _imp_LENGTHENI(int I) { return (long long int) I; }
static inline int __attribute__((always_inline)) _imp_SHORTENI(long long int I) { return (int) I; }

// 32 bit
static inline float * __attribute__((always_inline)) _imp_REAL(_imp_address N) {
  return (float *)N;
}

// 64 bit
static inline double * __attribute__((always_inline)) _imp_LONGREAL(_imp_address N) {
  return (double *)N;
}

// 128 bit
static inline long double * __attribute__((always_inline)) LONGLONGREAL(_imp_address N) {
  return (long double *)N;
}

static inline long double __attribute__((always_inline)) _imp_LENGTHENR(double R) { return (long double) R; }
static inline double __attribute__((always_inline)) _imp_SHORTHENR(long double R) { return (double) R; }


static inline _imp_string * __attribute__((always_inline)) _imp_STRING(_imp_address N) {
  return (_imp_string *)N;
}

static inline void * __attribute__((always_inline)) _imp_RECORD(_imp_address N) {
  return (void *)N; // Needs compiler support with casting at the point of call...
}

/*
   Most math operations have multiple forms depending on the parameter/result type:

   float powf(float x, float y);
   double pow(double x, double y);
   long double powl(long double x, long double y);
 */

static inline double                   __attribute__((always_inline)) _imp_ARCSIN(double ANGLE) { extern double asin(double x); return asin(ANGLE); }
static inline double                   __attribute__((always_inline)) _imp_ARCCOS(double ANGLE) { extern double acos(double x); return acos(ANGLE); }
static inline double                   __attribute__((always_inline)) _imp_ARCTAN(double X, double Y) { extern double atan2(double y, double x); return atan2(X, Y); }
static inline double                   __attribute__((always_inline)) _imp_ARCTAN1(double ANGLE) { extern double atan(double x); return atan(ANGLE); }

static inline double                   __attribute__((always_inline)) _imp_SIN(double ANGLE) { extern double sin(double x); return sin(ANGLE); }
static inline double                   __attribute__((always_inline)) _imp_COS(double ANGLE) { extern double cos(double x); return cos(ANGLE); }
static inline double                   __attribute__((always_inline)) _imp_TAN(double ANGLE) { extern double tan(double x); return tan(ANGLE); }
static inline double                   _imp_COT(double ANGLE) {
  extern double sin(double x);
  extern double cos(double x);
  extern double fabs(double x);
  double sin_val = sin(ANGLE);
    
  // Check if denominator is close to zero
  if (fabs(sin_val) < 1e-15) { 
    // TO DO: _imp_signal(?,, ?, ?, "cot angle is a multiple of Pi");
    return _imp_INFINITY; 
  }
    
  return cos(ANGLE) / sin_val;
}

static inline double                   __attribute__((always_inline)) _imp_FRACTION(double R) { return R-_imp_TRUNC(R); }
static inline double                   __attribute__((always_inline)) _imp_FRACPT(double R) { return R-_imp_INTPT(R); }

static inline int                      __attribute__((always_inline)) _imp_IMOD(int I) { extern int abs(int j); return abs(I); }
static inline double                   __attribute__((always_inline)) _imp_MOD(double R) { extern double fabs(double x); return fabs(R); }

static inline int                      __attribute__((always_inline)) _imp_INT(double R) { return _imp_INTPT(R+0.5); }
static inline int                      __attribute__((always_inline)) _imp_INTPT(double R) { extern double floor(double x); /*assert(floor(R) <= R);*/ return (int)floor(R); } // rounds *towards minus infinity*

static inline long int                 __attribute__((always_inline)) _imp_LINT(double R) { return _imp_LINTPT(R+0.5); }
static inline long int                 __attribute__((always_inline)) _imp_LINTPT(double R) { extern double floor(double x); /*assert(floor(R) <= R);*/ return (long int)floor(R); }

static inline int                      __attribute__((always_inline)) _imp_TRUNC(double R) { extern double trunc(double x); return (int)trunc(R); } // rounds *towards 0*
static inline int                      __attribute__((always_inline)) _imp_ROUND(double R) { return _imp_TRUNC(R < 0.0 ? R-0.5 : R+0.5); }

static inline long long int            _imp_IEXP(int NUM, int POWER) {
                                         //extern double exp(double x);
                                         int tot = 1, mul = POWER;
                                         while (mul-- > 0) tot *= NUM;
                                         return tot; // return (long)exp(NUM, POWER);
                                       }

static inline double                   _imp_REXP(double NUM, double POWER) {
                                         extern double pow(double x, double y);
                                         return POWER==2 ? NUM*NUM : pow(NUM,POWER);
                                       }

static inline double                   __attribute__((always_inline)) _imp_EXP(double NUM) {
                                         extern double exp(double x);
                                         return exp(NUM);
                                       }

static inline double                   __attribute__((always_inline)) _imp_LOG(double X) { extern double log(double x); return log(X); }
static inline int                      __attribute__((always_inline)) _imp_REM(int P, int Q) { return P % Q; }
static inline double                   __attribute__((always_inline)) _imp_SQRT(double NUM) { extern double sqrt(double x); return sqrt(NUM); }
static inline double                   _imp_RADIUS(double X, double Y) { extern double sqrt(double x); return sqrt(X*X+Y*Y); }
static inline int                      __attribute__((always_inline)) _imp_ISQRT(int NUM) { extern double sqrt(double x); return (int)sqrt(NUM); }
static inline int                      __attribute__((always_inline)) _imp_MULDIV(int A, int B, int C) { return (int) (((long long)A * (long long)B) / (long long)C); }

static inline void                     _imp_PROMPT(_imp_string15 S) { check_instream(__PRETTY_FUNCTION__); _imp_promptstr = *(_imp_string255 *)&S; }
static inline void                     __attribute__((always_inline)) _imp_READSYMBOL(int *P) { _imp_readsymbol(P); }
static inline void                     __attribute__((always_inline)) _imp_READCH(int *P) { _imp_readch(P); }
static inline int                      __attribute__((always_inline)) _imp_NEXTSYMBOL(void) { return _imp_nextsymbol(); }
static inline int                      __attribute__((always_inline)) _imp_NEXTCH(void) { return _imp_nextch(); }
static inline void                     __attribute__((always_inline)) _imp_SKIPSYMBOL(void) { _imp_skipsymbol(); }
static inline void                     __attribute__((always_inline)) _imp_PRINTSYMBOL(char SYM) { _imp_printsymbol(SYM); }
static inline void                     __attribute__((always_inline)) _imp_PRINTCH(char SYM) { _imp_printch(SYM); }

static inline void                     __attribute__((always_inline)) _imp_PRINTSTRING(_imp_string S) { _imp_printstring(S); }
// WAS:
//#define _imp_PRINTSTRING(S) _imp_printstring_ptr((_imp_string *)&S)
/*
#define _imp_PRINTSTRING(S) ({                \
  __typeof__(S) _tmp_val = (S); \
  _imp_printstring_ptr((_imp_string *)&_tmp_val); \
})
*/

// experimenting with different ways to implement longinteger:
#define _imp_WRITE(V, P) do { \
                              if (sizeof(V) == sizeof(long long int)) _imp_writelong((long long int)(V), P); else _imp_writeint((int)(V), P); \
                            } while (0)
//static inline void                     _imp_WRITE(int V, int P) { _imp_write(V, P); } // TO DO: longinteger

// IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}

static inline void                     __attribute__((always_inline)) _imp_READITEM(_imp_string *S) { _imp_readitem(S);  }
static inline void                     __attribute__((always_inline)) _imp_READSTRING(_imp_string *S) { _imp_readstring(S); }
static inline void                     __attribute__((always_inline)) _imp_READTEXT(_imp_string *S, int DELIM) { _imp_readtext(S, DELIM); }
static inline _imp_string1             __attribute__((always_inline)) _imp_NEXTITEM(void) { return _imp_nextitem(); }
static inline void                     __attribute__((always_inline)) _imp_READLINE(_imp_string *S) { _imp_readline(S); }

static inline void                     _imp_SETSTREAMS(void) {
  /* To do - not a serious implementation yet. */
  // Just enough to get layout.imp from the Dec10 running!
  _imp_OPENOUTPUT(2, _imp_str_literal("/dev/stdout"));
}

static inline int                      _imp_INSTREAM(void) { check_instream(__PRETTY_FUNCTION__); return _imp_InStream; }
static inline int                      _imp_OUTSTREAM(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_OutStream; }
static inline int                      _imp_OUTDEV(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_OutStream; }
static inline int                      _imp_INPUTSTREAM(void) { check_instream(__PRETTY_FUNCTION__); return _imp_InStream; }
static inline int                      _imp_OUTPUTSTREAM(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_OutStream; }
static inline _imp_string              _imp_INPUTNAME(void) { check_instream(__PRETTY_FUNCTION__); return _imp_infile[_imp_InStream].fname; }
static inline _imp_string              _imp_OUTPUTNAME(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_outfile[_imp_OutStream].fname; }
static inline _imp_string              _imp_INFILENAME(void) { check_instream(__PRETTY_FUNCTION__); return _imp_infile[_imp_InStream].fname; }
static inline _imp_string              _imp_OUTFILENAME(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_outfile[_imp_OutStream].fname; }
static inline void                     _imp_SELECTINPUT(int N) { if (N < 0) return; _imp_InStream = N; _imp_InFile = _imp_infile[N].f; check_instream(__PRETTY_FUNCTION__); }
static inline void                     _imp_SELECTOUTPUT(int N) { if (N < 0) return; _imp_OutStream = N; _imp_OutFile = _imp_outfile[N].f; check_outstream(__PRETTY_FUNCTION__); }
static inline void                     __attribute__((always_inline)) _imp_OPENINPUT(int N, _imp_string FD) { _imp_openinput(N, FD); }
static inline void                     __attribute__((always_inline)) _imp_OPENOUTPUT(int N, _imp_string FD) { _imp_openoutput(N, FD); }
static inline void                     __attribute__((always_inline)) _imp_OPENBINARYINPUT(int N, _imp_string FD) { _imp_openinput(N, FD); }
static inline void                     __attribute__((always_inline)) _imp_OPENBINARYOUTPUT(int N, _imp_string FD) { _imp_openoutput(N, FD); }
static inline void                     __attribute__((always_inline)) _imp_DEFINEINPUT(int I, _imp_string SPEC) { _imp_OPENINPUT(I, SPEC); }
static inline void                     __attribute__((always_inline)) _imp_DEFINEOUTPUT(int I, _imp_string SPEC) { _imp_OPENOUTPUT(I, SPEC); }
static inline void                     __attribute__((always_inline)) _imp_ABANDONINPUT(void) { _imp_abandoninput(); }
static inline void                     __attribute__((always_inline)) _imp_ABANDONOUTPUT(void) { _imp_abandonoutput(); }
static inline void                     __attribute__((always_inline)) _imp_CLOSESTREAM(int stream) { _imp_closestream(stream); } /* EMAS */
static inline void                     __attribute__((always_inline)) _imp_CLOSEINPUT(void) { _imp_closeinput(); }
static inline void                     __attribute__((always_inline)) _imp_CLOSEOUTPUT(void) { _imp_closeoutput(); }
static inline void                     __attribute__((always_inline)) _imp_RESETINPUT(void) { _imp_resetinput(); }
static inline void                     __attribute__((always_inline)) _imp_RESETOUTPUT(void) { check_outstream(__PRETTY_FUNCTION__); }
static inline void                     __attribute__((always_inline)) _imp_COMPLETEINPUT(void) { check_instream(__PRETTY_FUNCTION__); }
static inline void                     __attribute__((always_inline)) _imp_COMPLETEOUTPUT(void) { check_outstream(__PRETTY_FUNCTION__); }

// seek/tell:
static inline void                     __attribute__((always_inline)) _imp_POSITIONINPUT(int P) { _imp_SETINPUT(P); }
static inline void                     __attribute__((always_inline)) _imp_POSITIONOUTPUT(int P) { _imp_SETOUTPUT(P); }
static inline void                     __attribute__((always_inline)) _imp_SETINPUT(int P) { _imp_setinput(P); }
static inline void                     __attribute__((always_inline)) _imp_SETOUTPUT(int P) { _imp_setoutput(P) ; }
static inline void                     __attribute__((always_inline)) _imp_INPUTPOSITION(void) { check_instream(__PRETTY_FUNCTION__); }  // TO DO: is this also 'inpos' or is it the ftell file offset?
static inline void                     _imp_OUTPUTPOSITION(void) { check_outstream(__PRETTY_FUNCTION__); }
static inline int                      __attribute__((always_inline)) _imp_ENDOFINPUT(void) { return 0; /*TO DO*/}

static inline int                      _imp_INPOS(void) { check_instream(__PRETTY_FUNCTION__); return _imp_infile[_imp_InStream].inpos; }
static inline int                      _imp_OUTPOS(void) { check_outstream(__PRETTY_FUNCTION__); return _imp_outfile[_imp_OutStream].outpos; }

static inline void                     __attribute__((always_inline)) _imp_SPACE(void) { _imp_space(); }
static inline void                     __attribute__((always_inline)) _imp_SPACES(int N) { _imp_spaces(N); }
static inline void                     __attribute__((always_inline)) _imp_NEWPAGE(void) { _imp_newpage(); }
static inline void                     __attribute__((always_inline)) _imp_NEWLINE(void) { _imp_newline(); }
static inline void                     __attribute__((always_inline)) _imp_NEWLINES(int N) { _imp_newlines(N); }
static inline void                     __attribute__((always_inline)) _imp_PRINT(double R, int BEFORE, int AFTER) { _imp_print(R, BEFORE, AFTER); }        // IMPORTANT: See https://gtoal.com/imp77/reference-manual/IO-LIBRARY-EXPERIMENTS.{imp,txt}
static inline void                     __attribute__((always_inline)) _imp_PRINTFLOATING(double R, int A, int B) { _imp_printfloating(R, A, B); }
static inline void                     __attribute__((always_inline)) _imp_PRINTFL(double R, int PLACES) { _imp_printfl(R, PLACES); }
static inline void                     __attribute__((always_inline)) _imp_PRINTFHEX(double R) { _imp_printfhex(R); }

static inline _imp_string              __attribute__((always_inline)) _imp_SUBSTRING(_imp_string *S, int FROM, int TO) { return _imp_substring(S, FROM, TO); }
static inline _imp_string              __attribute__((always_inline)) _imp_FROMSTRING(_imp_string *S, int FROM, int TO) { return _imp_substring(S, FROM, TO); }
static inline _imp_string              __attribute__((always_inline)) _imp_TRIM(_imp_string S, int MAX) { return _imp_trim(S, MAX); }
static inline void                     __attribute__((always_inline)) _imp_TOUPPER(_imp_string *S) { return _imp_toupper(S); }
static inline void                     __attribute__((always_inline)) _imp_TOLOWER(_imp_string *S) { return _imp_tolower(S); }


static inline _imp_string8             __attribute__((always_inline)) _imp_TIME(void) { return _imp_time(); }
static inline _imp_string8             __attribute__((always_inline)) _imp_DATE(void) { return _imp_date(); }
static inline int                      __attribute__((always_inline)) _imp_CPUTIME(void) { return _imp_cputime(); }

static inline _imp_string              __attribute__((always_inline)) _imp_CLIPARAM(void) { return _imp_cliparam(); }

// FREESTORE
static inline int                      __attribute__((always_inline)) _imp_FREESTORE(void) { return 0x8000; }  // made up value for old Imp15 programs. Could do a real number for modern linux.

static inline _imp_string              __attribute__((always_inline)) _imp_ITOS(int I, int P) { return _imp_itos(I, P); }
static inline int                      __attribute__((always_inline)) _imp_STOI(_imp_string S) { return _imp_stoi(S); }
// there are some other calls like ITOS I think.  (Also check re ITOS2/ITOS)

// not to be confused with the EVENT_MESSAGE field.  This is specifically for native system calls that went wrong.
static inline _imp_string              __attribute__((always_inline)) _imp_SSFMESSAGE(void) { return _imp_ssfmessage(); }


//////////////////////////////////////////////////////////////////////////////////////////////


// These are the routines from JDM's imp2026 perms which I've added
// to my perms for his convenience when testing his programs under i2c.

// I'm not promising that these will be supported long-term...

// (The only reason for these const int fn shennanigans is to avoid
//  namespace pollution from %constinteger in the Imp perms...)

// How to implement %permconstinteger via C's inline functions!:
static inline const int __attribute__((always_inline)) _imp_FILESEP(void)     { return '='; }
static inline const int __attribute__((always_inline)) _imp_CMAXSIGFIGS(void) { return 15; }

static inline const int __attribute__((always_inline)) _imp_FROMSTART(void)   { return 0; }
static inline const int __attribute__((always_inline)) _imp_FROMCURRENT(void) { return 1; }
static inline const int __attribute__((always_inline)) _imp_FROMEND(void)     { return 2; }

extern const _imp_string _imp_typedesc[32]; // or move spec to ingternal.h?

static inline _imp_string _imp_TYPENAME(int TYPECODE) {
  return ((((0) >= (TYPECODE))) || (((TYPECODE) > (31))))
         ? _imp_str_literal("unknown")
         : _imp_typedesc[TYPECODE];
}

static inline void __attribute__((always_inline)) _imp_SEEKINPUT(int DISPLACEMENT, int STARTPOINT) {
  _imp_seekinput(DISPLACEMENT, STARTPOINT);
}
static inline void __attribute__((always_inline)) _imp_SEEKOUTPUT(int DISPLACEMENT, int STARTPOINT) {
  _imp_seekoutput(DISPLACEMENT, STARTPOINT);
}

static inline int __attribute__((always_inline)) _imp_TELLINPUT(void) {
  return _imp_tellinput();
}
static inline int __attribute__((always_inline)) _imp_TELLOUTPUT(void) {
  return _imp_telloutput();
}

static inline int _imp_READBUFFER(void *PTR, int COUNT) {
  int i;
  unsigned char *s;
  s = (unsigned char *)PTR;
  int ch;
  for (i = 0; i < COUNT; i++) {
    ch = _imp_getchar(_imp_InFile);
    if (ch == -1 /* EOF */) {
      //_imp_signal(9, 2, _imp_InStream, strerror(errno));
      //exit(EXIT_FAILURE);
      return i;
    }
    *s++ = ch;
  }
  return COUNT;
}

static inline _imp_string31 __attribute__((always_inline)) _imp_CURRENTPROMPT(void) { return *(_imp_string31 *)&_imp_promptstr ; }

static inline int __attribute__((always_inline)) _imp_GETMAXINPUTSTREAM(void) { return 255 ; }
static inline int __attribute__((always_inline)) _imp_GETMAXOUTPUTSTREAM(void) {  return 0; }

static inline int __attribute__((always_inline)) _imp_FILEEND(void) {
  return _imp_fileend();
}

// TO DO:
static inline void __attribute__((always_inline)) _imp_ALLOWEOFEVENT(void) {  ; }
static inline void __attribute__((always_inline)) _imp_BLOCKEOFEVENT(void) {  ; }

// TO DO:
static inline int __attribute__((always_inline)) _imp_OLDOUTPUTSTREAM(void) { return 0 ; }

static inline int __attribute__((always_inline)) _imp_WRITEBUFFER(void *PTR, int COUNT) {
  return _imp_writebuffer(PTR, COUNT);
}

static inline void __attribute__((always_inline)) _imp_FLUSHOUTPUT(void) { _imp_flushoutput(); }

static inline void __attribute__((always_inline)) _imp_PRINTREAL(double F, int SF, int PLACES) {_imp_print(F, SF, PLACES);}
static inline void __attribute__((always_inline)) _imp_PRINTFLOAT(double F, int SF) { _imp_printfl(F, SF) ; }

// TO DO: more number formatting but to strings rather than current output file
static inline _imp_string _imp_FTOS(double F, int SF) {
  return _imp_formatf("%*f", SF, F);
}

// Does C's printf handle bases???
extern char *_c_itoa(int value, char *dest, int base);
static inline _imp_string _imp_INT2ASCII(int NN, int BASE, int PLACES) {
  char buffer[256];
  _c_itoa(NN, buffer, BASE);
  return _imp_c2istr(buffer);
}
static inline _imp_string _imp_FORMATNUMBER(int N, int BASE, int PLACES) {
  char buffer[256];
  _c_itoa(N, buffer, BASE);
  return _imp_c2istr(buffer);
}

// User can print "16_" before this if that's what's wanted:
static inline _imp_string _imp_INT2HEX(int NN, int PLACES) {
  return _imp_formatf("%*A", PLACES, NN);
}

// User can print "2_" before these if that's what's wanted:
static inline _imp_string32 _imp_INT2BIN(int NN) {
  // AHA! This is what I have to do with _imp_string in order to assign strings of different lengths!
  typedef union {
    _imp_string32 str32;
    _imp_string255 str255;
  } s32;
  s32 tmp;
  tmp.str255 = _imp_formatf("%32b", NN);
  return tmp.str32;
}
static inline _imp_string16 _imp_SHORT2BIN(short NN) {
  typedef union {
    _imp_string16 str16;
    _imp_string255 str255;
  } s16;
  s16 tmp;
  tmp.str255 = _imp_formatf("%16b", NN);
  return tmp.str16;
}

// TO DO: %name & dopevector stuff
static inline int _imp_ARRAYENTRYSIZE(void *N) {
  _imp_signal(0,0,0, "ARRAYENTRYSIZE not yet implemented");
  _imp_stop(1); // short-term error message. Needs _imp_signal. *TO DO*
  return 0;
}
static inline int _imp_ARRAYENTRYTYPE(void *N) {
  _imp_signal(0,0,0, "ARRAYENTRYTYPE not yet implemented");
  _imp_stop(1); // short-term error message. Needs _imp_signal. *TO DO*
  return 0;
}
static inline int _imp_ARRAYDIMENSIONS(void *N) {
  _imp_signal(0,0,0, "ARRAYDIMENSIONS not yet implemented");
  _imp_stop(1); // short-term error message. Needs _imp_signal. *TO DO*
  return 1;
}
static inline int _imp_ARRAYLOWERBOUND(void *N, int INDEX) {
  _imp_signal(0,0,0, "ARRAYLOWERBOUND not yet implemented");
  _imp_stop(1); // short-term error message. Needs _imp_signal. *TO DO*
  return 0;
}
static inline int _imp_ARRAYUPPERBOUND(void *N, int INDEX) {
  _imp_signal(0,0,0, "ARRAYUPPERBOUND not yet implemented");
  _imp_stop(1); // short-term error message. Needs _imp_signal. *TO DO*
  return 0;
}

// TO DO:  Is this from envp, akin to argv?
static inline int __attribute__((always_inline)) _imp_GETENVCOUNT(void) { return 0 ; }
static inline _imp_string __attribute__((always_inline)) _imp_GETENVNAME(int INDEX) { return (_imp_string){0} ; }
static inline _imp_string __attribute__((always_inline)) _imp_GETENV(int INDEX) { return (_imp_string){0} ; }
static inline int __attribute__((always_inline)) _imp_GETENVINDEX(_imp_string VARNAME) { return 0 ; }
static inline _imp_string __attribute__((always_inline)) _imp_GETENVASSTRING(_imp_string ENVNAME) { return (_imp_string){0} ; }
static inline int __attribute__((always_inline)) _imp_GETENVASINTEGER(_imp_string VARNAME) { return 0 ; }


static inline int __attribute__((always_inline)) _imp_GETARGCOUNT(void) { return _imp_ARGC() ; }
static inline _imp_string __attribute__((always_inline)) _imp_GETARG(int INDEX) { return _imp_ARGV(INDEX) ; }
static inline double __attribute__((always_inline)) _imp_SINE(double X) { extern double sin(double x); return sin(X) ; }
static inline double __attribute__((always_inline)) _imp_COSINE(double X) { extern double cos(double x); return cos(X) ; }
static inline double __attribute__((always_inline)) _imp_LOG2(double X) { extern double log2(double x); return log2(X) ; }
static inline double __attribute__((always_inline)) _imp_LOG10(double X) { extern double log10(double x); return log10(X) ; }
static inline void __attribute__((always_inline)) _imp_EXIT(int STATUS) { _imp_stop(STATUS) ; }
static inline void __attribute__((always_inline)) _imp_stop(int STATUS) { extern void exit(int STATUS); exit(STATUS) ; }

// Evaluate the string only once to avoid unintended side-effects:
#define _imp_check_strname(STR, LEN)     *({void *STRPTR  =   STR ; if (*_imp_LENGTH(STRPTR)  > LEN) _imp_signal(6,1,LEN, "STRING CAPACITY EXCEEDED");  STRPTR ;})
#define _imp_check_strvalue(STRPTR, LEN) ({void *STRPTR2 = STRPTR;  if (*_imp_LENGTH(STRPTR2) > LEN) _imp_signal(6,1,LEN, "STRING CAPACITY EXCEEDED"); *(_imp_string##LEN *)STRPTR2 ;})

#endif // __VISIBLE_H__
