Yes, but it is unsupported. The expression $ZUTIL(70,2,value) will return value encoded for use as a subscript subject to the current default subscript encoding. You can combine this with $LENGTH(), so $LENGTH($ZUTIL(70,2,value)) to get the length of a subscript once encoded. This technique should never find its way into production code. However, if you just want to understand how various codepoints are encoded, you can use it for experimentation.

There is nothing built into Caché to get a updating virtual terminal window size like you would have with ncurses. You could...

(1) Write the terminal portions of you application in a language with ncurses support (like "C"), and then use either call-in or call-out to combine the ncurses portions of your application with the COS portions. Call-in would probably be easier.

(2) If you can live without an automated update, the traditional solution is to assign a key in your application to repaint the screen after modem line noise (remember that) messed-up the screen. Traditionally, that was <CTRL+R>. You could just add to the work done by <CTRL+R> to be.
    (a) Clear the screen.
    (b) Position the cursor at the lower right hand corner.
    (c) Clear the typeahead buffer.
    (d) Ask the terminal were its cursor is.
    (e) Read, and parse the Cursor Position Report.
    The position of the cursor is the window size.
    (f) Repaint the screen at its current size.
Step (a) could be moved later in the sequence if you like.
Steps (b) and (c) can be reversed.

If you only care about supporting modern terminal emulators, all of which emulate VT-100+ terminals, the code is

    ;(a) Clear the screen
    WRITE $CHAR(27),"[H"
    ;(b) Positions the cursor at the lower right hand corner.
    WRITE $CHAR(27)_"[255;255H"
    ;(c) Clear the typeahead buffer.
    WRITE *-1
    ;(d) Ask the terminal where its cursor is:
    ;Use this routine
FC()    NEW
    SET result=0 FOR j=1:1:4 DO  QUIT:result  ;             [10]
    . WRITE $CHAR(27)_"[6n" ;                               [20]
    . READ junk:j QUIT:'$TEST  QUIT:$ASCII($KEY)'=27  ;     [30]
    . QUIT:$EXTRACT($KEY,2)'="["  QUIT:$EXTRACT($KEY,*)'="R"
    . SET $Y=$EXTRACT($KEY,3,*)-1,$X=$PIECE($KEY,";",2)-1 ; [40]
    . SET result=1
    QUIT result
    ; ------------
    ; [10] Assume failure. Try four times (with increasing
    ;      timeout) to determine the location of the cursor.
    ;      Quit early on success.
    ; [20] Send where is the cursor (DSR Device Status Request).
    ; [30] Read result, CPR (Cursor Position Report). We must
    ;      get it, and it must be of form "<ESC>[#,#R".
    ; [40] Extract $Y and $X from the CPR (Cursor Position
    ;      Report), and call it a success.
    ;(e) Repaint the screen at its current size.
    ; That is up to you.

JSON format is probably the most general. Return the values in a JSON formatted string. Then parse with

    SET objresult=##CLASS(%DynamicObject).%FromJSON(result)

However, if you want to return a simple structure. That is two or a few values, where no values are themselves structures, and nature of the data is easily understood, you could return the values as an artificial local reference, and take the value apart with $QSUBSCRIPT() is COS. This function would prove handy for such an option.

/* This function adds %q to snprintf() to print a quoted string. */
int varcosreturn (char *buffer, size_t len, char *fmt, ...) {
   va_list ap;
   char *p, *q, *r;
   char c;
   size_t n;
   char xfmt [3];

   va_start(ap, fmt);
   p = buffer; q = fmt;
   for (;;) {
      c = *q++;
      if (c == '\0') break;
      if (c != '%') { if (len==0) break; --len; *p++ = c; continue; }
      c = *q++;
      if (c == 'q') {
         if (len==0) break; --len; *p++ = '\"';
         r = va_arg(ap,char*);
         for (;;) {
            c = *r++;
            if (c == '\0') break;
            if (c == '\"') { if (len==0) break; --len; *p++ = '\"'; }
            if (len==0) break; --len; *p++ = c;
         }
         if (len==0) break; --len; *p++ = '\"';
         continue;
      }
      xfmt [0] = '%'; xfmt [1] = c; xfmt [2] ='\0';
      n = snprintf (p, len, xfmt, va_arg(ap, void*));
      len -= n; p += n;
   }
   va_end(ap);
   if (len==0) return -1; --len; *p++ = '\0';
   return 0;
}

$LISTBUID() format is not doucmented so that InterSystems can later expand (or change) it.

The problem is both OPEN statements are for TCP/IP servers. One OPEN has to be a client. You distinguish between the two by giving a DNS or IP address in the OPEN. If everything is running locally, use "127.0.0.1", So

     OPEN dev:("127.0.0.1":33568):3 

Also on one OPEN us are using "\n" for the terminators argument. That makes reads break on backslash and lowercase n. Is that really what you want? This isn't "C". Generally you should specify a mode for TCP/IP I/O. For text based messaging over TCP/IP "PTSE" ≣ "M" works best. Add an "A", that is "MA", for a multi-server. For a binary channel, using mode "S" is still a good idea. Thus:

     OPEN dev:(:33568:"M"):3 ; Open the server.

     OPEN dev:("127.0.0.1":33568:"M"):3 ; Open the client.

IJC devices work! They come in pairs, and all may not be defined on your system. Start at the beginning and try writing to 225 and reading from 224. Each process must OPEN their devices before reading or writing. You get to write a certain amount to 225 before the device blocks. The reader can read with a zero second time-out, if you don't want the reader waiting. 

In the system management portal goto
System Administration → Configuration → Additional Settings → Advanced Memory. ijcbuff controls the amount of memory per IJC device. The bigger the more you can write to an IJC device with an lagging reader, before the device blocks. ijcnum is the number of defined IJC device pairs.

Calling $ZUTIL(132) with no extra arguments makes the current device the principal device. It was documented in the Caché ObjectScript Language Reference up until Caché 2009.1. It has been replaced with ##CLASS(%Device).ChangePrincipal(), which does the same thing. This isn't typically very useful.

Often more useful is the former $ZUTIL(82,12,bool), now ##CLASS(%Device).ReDirectIO(bool) which lets you redirect I/O through routines that can filter, redirect, record, though routines. Unfortunately, while the workings of $ZUTIL(82,12,bool) did eventually make it into the documentation, the workings were removed from the ##CLASS(%Device).ReDirectIO(bool) documentation. The details are the same, look at the old documentation on-line.

I have no idea what your code is really trying to do. Some comments would be helpful. Même un commentaire en français serait utile. In addition to adding parenthesis needed because COS does not have operator precedence. Also,

1. $a($e(string,i)) is unnecessary, $a(string,i) does the same thing.

2. You are obviously doing some kind of translation. Look at $ZCONVERT() in the documentation.

3. For other simple translation, $TRANSLATE() is a simple solution. For example if you have old data in AFNOR NF Z 62010-1982, you could translate it to Unicode with $TRANSLATE(string,"#'@[\]`{|}~","£’à°ç§µéùè¨").

Once your process receives a <STORE> error, it is almost out of memory. It is almost too late for the process to do any self-analysis as to the source of its problem. Still, if you use InterSystems ^%ETN error trap, you might have local variables, and they may show a pattern of waste.

If you believe you know approximately where your local variable waste is located, you can add debug statements like:

SET ^%DEBUG($JOB,$I(^%DEBUG))=$ZDT($H,3,1)_" Now at mumble "_$S

A more advanced approach is to run the code under trace, watching $STORAGE change with each line. Like this:

USER>SET f="/Users/salzer/Desktop/storage.txt"
USER>ZBREAK TRACE:all:f
USER>ZBREAK $:"T"::"WRITE $STORAGE"
USER>DO ^MEMORYLEAK
USER>ZBREAK /TRACE:OFF

Doing this your application will run rather slow. But if it is a batch load, that is fine. Batch loaders don't usually include time-outs. The result is a file that traces your application application and the memory it uses. Here is the start of a program to analyse that trace:

ZSTORE   ; SRS 2016-11-16
   KILL ^||ZSTORE
   SET f="/Users/salzer/Desktop/storage.txt"
   CLOSE f OPEN f:"RS":1 IF '$TEST { WRITE !,"Can't open ",f QUIT }
   USE f SET label="",last=0
   TRY {
     FOR i=0:1 {
       READ x
       IF x["Trace: ZBREAK at " { SET label=$EXTRACT(x,18,*) CONTINUE }
       IF last>0 {
         SET used=x-last,^||ZSTORE(label,$INCREMENT(^||ZSTORE))=used
       }
       SET last=+x
     }
   }
   CATCH err { }
   CLOSE f
   SET label="" WHILE 1 {
     SET label=$ORDER(^||ZSTORE(label)) IF label="" { QUIT }
     WRITE !,label
     SET i="",np=0 FOR n=0:1 {
       SET i=$ORDER(^||ZSTORE(label,i),1,v) IF i="" { QUIT }
       IF v'>0 { CONTINUE }
       IF np=0 { SET min=v,max=v,sum=v,np=1 CONTINUE }
       SET np=np+1
       IF v<min { SET min=v }
       IF v>max { SET max=v }
       SET sum=sum+v
     }
     WRITE " hits:",n IF np'>0 { CONTINUE }
     WRITE " positive:",np," min:",min," max:",max," avg:",sum/np
   }
   QUIT

My colleague provided you a very detailed answer to your first request. That is the “Why” part of your request. For the “How” part, rather than round away the the apparent error that you don’t understand, there is a whole branch of mathematics dealing with transforming your math to make it work on computers. While many programming languages optimize code to correct inefficient logic, few (including COS) will fix your math.

Some, simple examples are:

(1) When adding a vector of floating point numbers, it is wise to sort the numbers by their absolute value, then and add the small numbers to the accumulator first.

(2) When evaluating a polynomial like a·x³+b·x²+c·x+d, rewrite this as ((a·x+b)·x+c)·x+d, and this can be conveniently be written in COS without any parenthesis: SET ans=a*x+b*x+c*x+d

(3) Your problem: Divide last, unless it would cause an overflow. That is if your general equation is (x÷y)·z, why not rewrite it as (x·z)÷y. If (x·z) would cause an overflow, then use either (x÷y)·z, or (z÷y)·x, which ever gives a better answer. The simple way to decide which answer is better, is which answer has the fewest digits after the decimal point. Here is some code if not resorting to rounding is important to you.

MATH    ; SRS 2016-11-03
    FOR {
      READ !,"x/y*z enter: ",t  QUIT:t=""
      SET x=$PIECE(t,"/"),t=$PIECE(t,"/",2)
      SET y=$PIECE(t,"*"),z=$PIECE(t,"*",2)
      IF x'=+x||(y'=+y)||(z'=+z) { SET ans=$CHAR(9785) }
      ELSE {
        TRY { SET ans=x*z/y }
        CATCH e {
          TRY {
        SET xovery=x/y,zovery=z/y
        IF $LENGTH($PIECE(xovery,".",2))<$LENGTH($PIECE(zovery,".",2)) {
          SET ans=xovery*z
        } ELSE {
          SET ans=zovery*x
        }
          CATCH e {
            SET ans=$CHAR(8734)
          }
        }
      }
      WRITE " = ",ans
    }
    QUIT