/*
 * JavaCard software implementation of SHA2-384 and SHA2-512 as defined in FIPS PUB 180-2.
 * Based on source code from BouncyCastle (www.bouncycastle.org) and jsSha project ttp://jssha.sourceforge.net/
 * Ported by Petr Svenda (petr@svenda.com)
 */

/*
* Test vectors: "" empty string =>
* SHA-512: cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
*/

//package anonlogjclient;
package anoncard;
import javacard.framework.*;
import javacard.security.*;

public class Sha2 extends MessageDigest {
//public class Sha2 {
  public static final byte SHA_384 = (byte) 5;
  public static final byte SHA_512 = (byte) 6;
  public static final short SHA2_BLOCK_LENGTH = 128; // in bytes
  public static final short SHA512_DIGEST_LENGTH = 64; // in bytes
  public static final short SHA384_DIGEST_LENGTH = 48; // in bytes
  public static final short LONG_LENGTH = 8; // in bytes

  public static final short NUM_INT64_VARIABLES = 25;

  private byte    m_shaType = SHA_512;
  private byte[]  xBuf = null;

  public Int_64 K[] = null;
  Int_64 H[] = null;
  short[] W = null;

  public short int64Variables[] = null;

  public static final short OFFSET_a = (short) 0;
  public static final short OFFSET_b = (short) 4;
  public static final short OFFSET_c = (short) 8;
  public static final short OFFSET_d = (short) 12;
  public static final short OFFSET_e = (short) 16;
  public static final short OFFSET_f = (short) 20;
  public static final short OFFSET_g = (short) 24;
  public static final short OFFSET_h = (short) 28;
  public static final short OFFSET_T1 = (short) 32;
  public static final short OFFSET_T2 = (short) 36;

  public static final short OFFSET_result1 = (short) 40;
  public static final short OFFSET_result2 = (short) 44;
  public static final short OFFSET_result3 = (short) 48;
  public static final short OFFSET_result4 = (short) 52;

  public static final short OFFSET_rotl_1 = (short) 56;
  public static final short OFFSET_rotl_2 = (short) 60;
  public static final short OFFSET_rotl_3 = (short) 64;

  public static final short OFFSET_rotr_1 = (short) 68;
  public static final short OFFSET_rotr_2 = (short) 72;
  public static final short OFFSET_rotr_3 = (short) 76;

  public static final short OFFSET_safeAdd = (short) 80;
  public static final short OFFSET_safeAdd2 = (short) 84;
  public static final short OFFSET_Sigma0 = (short) 88;
  public static final short OFFSET_Sigma1 = (short) 92;

  public static final short OFFSET_xBufOff = (short) 96;
  public static final short OFFSET_wOff = (short) 97;
  public static final short OFFSET_byteCount1 = (short) 98;
  public static final short OFFSET_byteCount2 = (short) 99;

/*
short rotr_counter = 0;
short rotl_counter = 0;
short ch_counter = 0;
short maj_counter = 0;
short sigma0_counter = 0;
short sigma1_counter = 0;
short sum0_counter = 0;
short sum1_counter = 0;
short add_counter = 0;
/**/

public Sha2(byte shaType){
  this.m_shaType = shaType;

  int64Variables = JCSystem.makeTransientShortArray((short) (NUM_INT64_VARIABLES * 4), JCSystem.CLEAR_ON_RESET);
//int64Variables = new short[(short) (NUM_INT64_VARIABLES * 4)];

  xBuf = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET);
//xBuf = new byte[8];

// TODO - MOVE VALUES INTO short[] ARRAY AND INTO RAM MEMORY (if available)
  K = new Int_64[80];
  K[0] = new Int_64((short) 0x428a,(short) 0x2f98,(short) 0xd728,(short) 0xae22);
  K[1] = new Int_64((short) 0x7137,(short) 0x4491,(short) 0x23ef,(short) 0x65cd);
  K[2] = new Int_64((short) 0xb5c0,(short) 0xfbcf,(short) 0xec4d,(short) 0x3b2f);
  K[3] = new Int_64((short) 0xe9b5,(short) 0xdba5,(short) 0x8189,(short) 0xdbbc);
  K[4] = new Int_64((short) 0x3956,(short) 0xc25b,(short) 0xf348,(short) 0xb538);
  K[5] = new Int_64((short) 0x59f1,(short) 0x11f1,(short) 0xb605,(short) 0xd019);
  K[6] = new Int_64((short) 0x923f,(short) 0x82a4,(short) 0xaf19,(short) 0x4f9b);
  K[7] = new Int_64((short) 0xab1c,(short) 0x5ed5,(short) 0xda6d,(short) 0x8118);
  K[8] = new Int_64((short) 0xd807,(short) 0xaa98,(short) 0xa303,(short) 0x0242);
  K[9] = new Int_64((short) 0x1283,(short) 0x5b01,(short) 0x4570,(short) 0x6fbe);
  K[10] = new Int_64((short) 0x2431,(short) 0x85be,(short) 0x4ee4,(short) 0xb28c);
  K[11] = new Int_64((short) 0x550c,(short) 0x7dc3,(short) 0xd5ff,(short) 0xb4e2);
  K[12] = new Int_64((short) 0x72be,(short) 0x5d74,(short) 0xf27b,(short) 0x896f);
  K[13] = new Int_64((short) 0x80de,(short) 0xb1fe,(short) 0x3b16,(short) 0x96b1);
  K[14] = new Int_64((short) 0x9bdc,(short) 0x06a7,(short) 0x25c7,(short) 0x1235);
  K[15] = new Int_64((short) 0xc19b,(short) 0xf174,(short) 0xcf69,(short) 0x2694);
  K[16] = new Int_64((short) 0xe49b,(short) 0x69c1,(short) 0x9ef1,(short) 0x4ad2);
  K[17] = new Int_64((short) 0xefbe,(short) 0x4786,(short) 0x384f,(short) 0x25e3);
  K[18] = new Int_64((short) 0x0fc1,(short) 0x9dc6,(short) 0x8b8c,(short) 0xd5b5);
  K[19] = new Int_64((short) 0x240c,(short) 0xa1cc,(short) 0x77ac,(short) 0x9c65);
  K[20] = new Int_64((short) 0x2de9,(short) 0x2c6f,(short) 0x592b,(short) 0x0275);
  K[21] = new Int_64((short) 0x4a74,(short) 0x84aa,(short) 0x6ea6,(short) 0xe483);
  K[22] = new Int_64((short) 0x5cb0,(short) 0xa9dc,(short) 0xbd41,(short) 0xfbd4);
  K[23] = new Int_64((short) 0x76f9,(short) 0x88da,(short) 0x8311,(short) 0x53b5);
  K[24] = new Int_64((short) 0x983e,(short) 0x5152,(short) 0xee66,(short) 0xdfab);
  K[25] = new Int_64((short) 0xa831,(short) 0xc66d,(short) 0x2db4,(short) 0x3210);
  K[26] = new Int_64((short) 0xb003,(short) 0x27c8,(short) 0x98fb,(short) 0x213f);
  K[27] = new Int_64((short) 0xbf59,(short) 0x7fc7,(short) 0xbeef,(short) 0x0ee4);
  K[28] = new Int_64((short) 0xc6e0,(short) 0x0bf3,(short) 0x3da8,(short) 0x8fc2);
  K[29] = new Int_64((short) 0xd5a7,(short) 0x9147,(short) 0x930a,(short) 0xa725);
  K[30] = new Int_64((short) 0x06ca,(short) 0x6351,(short) 0xe003,(short) 0x826f);
  K[31] = new Int_64((short) 0x1429,(short) 0x2967,(short) 0x0a0e,(short) 0x6e70);
  K[32] = new Int_64((short) 0x27b7,(short) 0x0a85,(short) 0x46d2,(short) 0x2ffc);
  K[33] = new Int_64((short) 0x2e1b,(short) 0x2138,(short) 0x5c26,(short) 0xc926);
  K[34] = new Int_64((short) 0x4d2c,(short) 0x6dfc,(short) 0x5ac4,(short) 0x2aed);
  K[35] = new Int_64((short) 0x5338,(short) 0x0d13,(short) 0x9d95,(short) 0xb3df);
  K[36] = new Int_64((short) 0x650a,(short) 0x7354,(short) 0x8baf,(short) 0x63de);
  K[37] = new Int_64((short) 0x766a,(short) 0x0abb,(short) 0x3c77,(short) 0xb2a8);
  K[38] = new Int_64((short) 0x81c2,(short) 0xc92e,(short) 0x47ed,(short) 0xaee6);
  K[39] = new Int_64((short) 0x9272,(short) 0x2c85,(short) 0x1482,(short) 0x353b);
  K[40] = new Int_64((short) 0xa2bf,(short) 0xe8a1,(short) 0x4cf1,(short) 0x0364);
  K[41] = new Int_64((short) 0xa81a,(short) 0x664b,(short) 0xbc42,(short) 0x3001);
  K[42] = new Int_64((short) 0xc24b,(short) 0x8b70,(short) 0xd0f8,(short) 0x9791);
  K[43] = new Int_64((short) 0xc76c,(short) 0x51a3,(short) 0x0654,(short) 0xbe30);
  K[44] = new Int_64((short) 0xd192,(short) 0xe819,(short) 0xd6ef,(short) 0x5218);
  K[45] = new Int_64((short) 0xd699,(short) 0x0624,(short) 0x5565,(short) 0xa910);
  K[46] = new Int_64((short) 0xf40e,(short) 0x3585,(short) 0x5771,(short) 0x202a);
  K[47] = new Int_64((short) 0x106a,(short) 0xa070,(short) 0x32bb,(short) 0xd1b8);
  K[48] = new Int_64((short) 0x19a4,(short) 0xc116,(short) 0xb8d2,(short) 0xd0c8);
  K[49] = new Int_64((short) 0x1e37,(short) 0x6c08,(short) 0x5141,(short) 0xab53);
  K[50] = new Int_64((short) 0x2748,(short) 0x774c,(short) 0xdf8e,(short) 0xeb99);
  K[51] = new Int_64((short) 0x34b0,(short) 0xbcb5,(short) 0xe19b,(short) 0x48a8);
  K[52] = new Int_64((short) 0x391c,(short) 0x0cb3,(short) 0xc5c9,(short) 0x5a63);
  K[53] = new Int_64((short) 0x4ed8,(short) 0xaa4a,(short) 0xe341,(short) 0x8acb);
  K[54] = new Int_64((short) 0x5b9c,(short) 0xca4f,(short) 0x7763,(short) 0xe373);
  K[55] = new Int_64((short) 0x682e,(short) 0x6ff3,(short) 0xd6b2,(short) 0xb8a3);
  K[56] = new Int_64((short) 0x748f,(short) 0x82ee,(short) 0x5def,(short) 0xb2fc);
  K[57] = new Int_64((short) 0x78a5,(short) 0x636f,(short) 0x4317,(short) 0x2f60);
  K[58] = new Int_64((short) 0x84c8,(short) 0x7814,(short) 0xa1f0,(short) 0xab72);
  K[59] = new Int_64((short) 0x8cc7,(short) 0x0208,(short) 0x1a64,(short) 0x39ec);
  K[60] = new Int_64((short) 0x90be,(short) 0xfffa,(short) 0x2363,(short) 0x1e28);
  K[61] = new Int_64((short) 0xa450,(short) 0x6ceb,(short) 0xde82,(short) 0xbde9);
  K[62] = new Int_64((short) 0xbef9,(short) 0xa3f7,(short) 0xb2c6,(short) 0x7915);
  K[63] = new Int_64((short) 0xc671,(short) 0x78f2,(short) 0xe372,(short) 0x532b);
  K[64] = new Int_64((short) 0xca27,(short) 0x3ece,(short) 0xea26,(short) 0x619c);
  K[65] = new Int_64((short) 0xd186,(short) 0xb8c7,(short) 0x21c0,(short) 0xc207);
  K[66] = new Int_64((short) 0xeada,(short) 0x7dd6,(short) 0xcde0,(short) 0xeb1e);
  K[67] = new Int_64((short) 0xf57d,(short) 0x4f7f,(short) 0xee6e,(short) 0xd178);
  K[68] = new Int_64((short) 0x06f0,(short) 0x67aa,(short) 0x7217,(short) 0x6fba);
  K[69] = new Int_64((short) 0x0a63,(short) 0x7dc5,(short) 0xa2c8,(short) 0x98a6);
  K[70] = new Int_64((short) 0x113f,(short) 0x9804,(short) 0xbef9,(short) 0x0dae);
  K[71] = new Int_64((short) 0x1b71,(short) 0x0b35,(short) 0x131c,(short) 0x471b);
  K[72] = new Int_64((short) 0x28db,(short) 0x77f5,(short) 0x2304,(short) 0x7d84);
  K[73] = new Int_64((short) 0x32ca,(short) 0xab7b,(short) 0x40c7,(short) 0x2493);
  K[74] = new Int_64((short) 0x3c9e,(short) 0xbe0a,(short) 0x15c9,(short) 0xbebc);
  K[75] = new Int_64((short) 0x431d,(short) 0x67c4,(short) 0x9c10,(short) 0x0d4c);
  K[76] = new Int_64((short) 0x4cc5,(short) 0xd4be,(short) 0xcb3e,(short) 0x42b6);
  K[77] = new Int_64((short) 0x597f,(short) 0x299c,(short) 0xfc65,(short) 0x7e2a);
  K[78] = new Int_64((short) 0x5fcb,(short) 0x6fab,(short) 0x3ad6,(short) 0xfaec);
  K[79] = new Int_64((short) 0x6c44,(short) 0x198c,(short) 0x4a47,(short) 0x5817);

  H = new Int_64[8];
  for (byte i=0; i < (short) H.length; i++) H[i] = new Int_64();
  clearState(m_shaType);

  //W = new short[(short) (80 * 4)];
  W = JCSystem.makeTransientShortArray((short) (80 * 4), JCSystem.CLEAR_ON_RESET);
}

public void int64Set(short[] array, short offset, short highest, short higher, short lower, short lowest) {
  array[offset] = highest;
  array[(short)(offset + 1)] = higher;
  array[(short)(offset + 2)] = lower;
  array[(short)(offset + 3)] = lowest;
}

public void int64Set(short[] destArray, short destOffset, short[] srcArray, short srcOffset) {
  destArray[destOffset] = srcArray[srcOffset]; destArray[(short) (destOffset + 1)] = srcArray[(short) (srcOffset + 1)]; destArray[(short) (destOffset + 2)] = srcArray[(short) (srcOffset + 2)]; destArray[(short) (destOffset + 3)] = srcArray[(short) (srcOffset + 3)];
}

private void int64Set(short[] array, short offset, Int_64 template) {
  array[offset] = template.highest;
  array[(short)(offset + 1)] = template.higher;
  array[(short)(offset + 2)] = template.lower;
  array[(short)(offset + 3)] = template.lowest;
}

private void int64Set(short[] array, short offset, byte[] srcArray, short srcOffset) {
  array[(short) offset] = (short) (srcArray[srcOffset] << 8 | (short) ( srcArray[(short) (srcOffset + 1)] & 0xff));
  array[(short) (offset + 1)] = (short) (srcArray[(short) (srcOffset + 2)] << 8 |(short) ( srcArray[(short) (srcOffset + 3)] & 0xff));
  array[(short) (offset + 2)] = (short) (srcArray[(short) (srcOffset + 4)] << 8 | (short) ( srcArray[(short) (srcOffset + 5)] & 0xff));
  array[(short) (offset + 3)] = (short) (srcArray[(short) (srcOffset + 6)] << 8 | (short) ( srcArray[(short) (srcOffset + 7)] & 0xff));
}

void clearState(short variant) {
if (variant == SHA_384) {
        H[0].set((short) 0xcbbb, (short) 0x9d5d, (short) 0xc105, (short) 0x9ed8);
        H[1].set((short) 0x629a, (short) 0x292a, (short) 0x367c, (short) 0xd507);
        H[2].set((short) 0x9159, (short) 0x015a, (short) 0x3070, (short) 0xdd17);
        H[3].set((short) 0x152f, (short) 0xecd8, (short) 0xf70e, (short) 0x5939);
        H[4].set((short) 0x6733, (short) 0x2667, (short) 0xffc0, (short) 0x0b31);
        H[5].set((short) 0x8eb4, (short) 0x4a87, (short) 0x6858, (short) 0x1511);
        H[6].set((short) 0xdb0c, (short) 0x2e0d, (short) 0x64f9, (short) 0x8fa7);
        H[7].set((short) 0x47b5, (short) 0x481d, (short) 0xbefa, (short) 0x4fa4);
    }
    if (variant == SHA_512) {
        H[0].set((short) 0x6a09, (short) 0xe667, (short) 0xf3bc, (short) 0xc908);
        H[1].set((short) 0xbb67, (short) 0xae85, (short) 0x84ca, (short) 0xa73b);
        H[2].set((short) 0x3c6e, (short) 0xf372, (short) 0xfe94, (short) 0xf82b);
        H[3].set((short) 0xa54f, (short) 0xf53a, (short) 0x5f1d, (short) 0x36f1);
        H[4].set((short) 0x510e, (short) 0x527f, (short) 0xade6, (short) 0x82d1);
        H[5].set((short) 0x9b05, (short) 0x688c, (short) 0x2b3e, (short) 0x6c1f);
        H[6].set((short) 0x1f83, (short) 0xd9ab, (short) 0xfb41, (short) 0xbd6b);
        H[7].set((short) 0x5be0, (short) 0xcd19, (short) 0x137e, (short) 0x2179);
    }
}


public void update(byte in) {
  xBuf[int64Variables[OFFSET_xBufOff]++] = in;
  if (int64Variables[OFFSET_xBufOff] == (short) xBuf.length) {
    //processWord(xBuf, (short) 0);
    //W[int64Variables[OFFSET_wOff]].set(xBuf, (short) 0);
    int64Set(W, int64Variables[OFFSET_wOff], xBuf, (short) 0);
    int64Variables[OFFSET_wOff] += 4;
    if (int64Variables[OFFSET_wOff] == (short) (16 * 4)) processBlock();

    int64Variables[OFFSET_xBufOff] = 0;
  }
  int64Variables[OFFSET_byteCount1]++;
}
/**/
public void update(byte[] strToHash, short startOffset, short strLen) {
   //
   // fill the current word
   //
  while ((int64Variables[OFFSET_xBufOff] != 0) && (strLen > 0)) {
    //    update(strToHash[startOffset]);
    xBuf[int64Variables[OFFSET_xBufOff]++] = strToHash[startOffset];
    if (int64Variables[OFFSET_xBufOff] == (short) xBuf.length) {
      //processWord(xBuf, (short) 0);
      //    W[int64Variables[OFFSET_wOff]].set(xBuf, (short) 0);
      int64Set(W, int64Variables[OFFSET_wOff], xBuf, (short) 0);
      int64Variables[OFFSET_wOff] += 4;
      if (int64Variables[OFFSET_wOff] == (short) (16 * 4)) processBlock();

      int64Variables[OFFSET_xBufOff] = 0;
    }
    int64Variables[OFFSET_byteCount1]++;

    startOffset++;
    strLen--;
  }

//
// process whole words.
//
  while (strLen > xBuf.length)
  {
    //processWord(strToHash, startOffset);
//  W[int64Variables[OFFSET_wOff]].set(strToHash, startOffset);
    int64Set(W, int64Variables[OFFSET_wOff], strToHash, startOffset);
    int64Variables[OFFSET_wOff] += 4;
    if (int64Variables[OFFSET_wOff] == (short) (16 * 4)) processBlock();

    startOffset += xBuf.length;
    strLen -= xBuf.length;
    int64Variables[OFFSET_byteCount1] += xBuf.length;
  }

//
// load in the remainder.
//
  while (strLen > 0)
  {
//    update(strToHash[startOffset]);
    xBuf[int64Variables[OFFSET_xBufOff]++] = strToHash[startOffset];
    if (int64Variables[OFFSET_xBufOff] == (short) xBuf.length) {
      //processWord(xBuf, (short) 0);
//    W[int64Variables[OFFSET_wOff]].set(xBuf, (short) 0);
      int64Set(W, int64Variables[OFFSET_wOff], xBuf, (short) 0);
      int64Variables[OFFSET_wOff] += 4;
      if (int64Variables[OFFSET_wOff] == (short) (16 * 4)) processBlock();

      int64Variables[OFFSET_xBufOff] = 0;
    }
    int64Variables[OFFSET_byteCount1]++;

    startOffset++;
    strLen--;
  }
}

public short doFinal(byte[] strToHash, short startOffset, short strLen, byte[] hash, short hashOffset) {
// PROCESS GIVEN DATA
  update(strToHash, startOffset, strLen);

  int64Set(int64Variables, OFFSET_a, (short) 0, (short) 0, (short) 0, int64Variables[OFFSET_byteCount1]);
// shift length by 3
  rotl(OFFSET_a, (byte) 3, OFFSET_a);

//
// add the pad bytes.
//
  update((byte)128);
  while (int64Variables[OFFSET_xBufOff] != 0) update((byte)0);

  if (int64Variables[OFFSET_wOff] > (short) (14 * 4)) processBlock();
//W[14].set((short) 0, (short) 0, (short) 0, (short) 0);
  int64Set(W, (short) (14 * 4), (short) 0, (short) 0, (short) 0, (short) 0);
//W[15].set(int64Variables, OFFSET_a);
  int64Set(W, (short) (15 * 4), int64Variables, OFFSET_a);

  processBlock();

// RETRIEVE HASH
  switch (m_shaType) {
    case SHA_384: {
      //return[H[0].highOrder,H[0].lowOrder,H[1].highOrder,H[1].lowOrder,H[2].highOrder,H[2].lowOrder,H[3].highOrder,H[3].lowOrder,H[4].highOrder,H[4].lowOrder,H[5].highOrder,H[5].lowOrder];
      for (short i = 0; i < 6; i++) {
        H[i].get(hash, (short) (hashOffset + (short) (i * LONG_LENGTH)));
      }
      break;
    }
    case SHA_512: {
      //return[H[0].highOrder,H[0].lowOrder,H[1].highOrder,H[1].lowOrder,H[2].highOrder,H[2].lowOrder,H[3].highOrder,H[3].lowOrder,H[4].highOrder,H[4].lowOrder,H[5].highOrder,H[5].lowOrder,H[6].highOrder,H[6].lowOrder,H[7].highOrder,H[7].lowOrder];
      for (short i = 0; i < 8; i++) {
        H[i].get(hash, (short) (hashOffset + (short) (i * LONG_LENGTH)));
      }
      break;
    }
    default:return (short) -1;
  }

  reset();

  return (short) 0;
}

public void reset() {
  clearState(m_shaType);
  int64Variables[OFFSET_byteCount1] = 0;
  int64Variables[OFFSET_byteCount2] = 0;

  int64Variables[OFFSET_xBufOff] = 0;
  for (short i = 0; i < xBuf.length; i++) xBuf[i] = 0;

  int64Variables[OFFSET_wOff] = 0;
  for (short i = 0; i != (short) W.length; i++) W[i] = 0;
}
/*
protected void processWord(
byte[]  in,
short     inOff) {
W[int64Variables[OFFSET_wOff]].set(in, inOff);
int64Variables[OFFSET_wOff] += 4;
if (int64Variables[OFFSET_wOff] == (short) (16 * 4)) processBlock();
}
/**/

public byte getLength() {
  switch (m_shaType) {
    case SHA_512: return SHA512_DIGEST_LENGTH;
    case SHA_384: return SHA384_DIGEST_LENGTH;
  }

  return (short) 0;
}

public byte getDigestSize() {
    return getLength();
}

public byte getAlgorithm() {
  return m_shaType;
}
/* // THIS IS ORIGINAL VERSION OF SHA2 IMPLEMENTATION THAT WAS NOT ABLE TO PERFORM update() OPERATION
   // BUT ONLY WHOLE ARRAY HASHING AT ONCE. IT IS FASTER, BUT LESS FLEXIBLE.
   // NOTE: TO MAKE THIS WORKING, YOU NEED TO SET FIRST 16 VALUES OF W ARRAY in processBlock()!!!!!!
short getHash512(byte[] strToHash, short startOffset, short strLen, byte[] hash, short hashOffset) {
clearState(SHA_512);
coreSHA2(strToHash, startOffset, strLen, SHA_512, hash, hashOffset);

return SHA512_DIGEST_LENGTH;
}

short getHash384(byte[] strToHash, short startOffset, short strLen, byte[] hash, short hashOffset) {
clearState(SHA_512);
coreSHA2(strToHash, startOffset, strLen, SHA_384, hash, hashOffset);
return SHA384_DIGEST_LENGTH;
}

short coreSHA2(byte[] strToHash, short startOffset, short strLen, short variant, byte[] hash, short hashOffset){
  // PADDING
  // strLen + 2 * LONG_LENGTH  + 1 - 1 ... we must accomodate also 2 x 64bit value of size and starting padd value (0x80),
  // final -1 is to capture situation, when data + padding + length is exactly rounded to multiple of SHA-2 blocks
  short paddedLength = (short) (((short) ((short) ((short) (strLen + 2 * LONG_LENGTH + 1 - 1) / SHA2_BLOCK_LENGTH) * SHA2_BLOCK_LENGTH)) + SHA2_BLOCK_LENGTH);
  //short appendedMessageLength = paddedLength;
  strToHash[(short) (startOffset + strLen)] = (byte) 0x80;
  for (short i = (short) (strLen + 1); i < paddedLength; i++) strToHash[(short) (startOffset + i)] = 0;

  int64Set(int64Variables, OFFSET_a, (short) 0, (short) 0, (short) 0, strLen);
  // shift length by 3
  rotl(OFFSET_a, (byte) 3, OFFSET_a);

  strToHash[(short) (startOffset + paddedLength - 4)] = (byte) 0;
  strToHash[(short) (startOffset + paddedLength - 3)] = (byte) (int64Variables[(short) (OFFSET_a + 2)] & 0xff);
  strToHash[(short) (startOffset + paddedLength - 2)] = (byte) ((int64Variables[(short) (OFFSET_a + 3)] >> 8) & 0xff);
  strToHash[(short) (startOffset + paddedLength - 1)] = (byte) (int64Variables[(short) (OFFSET_a + 3)] & 0xff);

  for (short i=0; i< paddedLength; i += SHA2_BLOCK_LENGTH){
      processBlock(strToHash, (short) (startOffset + i));
  }

  switch (variant) {
      case SHA_384: {
          //return[H[0].highOrder,H[0].lowOrder,H[1].highOrder,H[1].lowOrder,H[2].highOrder,H[2].lowOrder,H[3].highOrder,H[3].lowOrder,H[4].highOrder,H[4].lowOrder,H[5].highOrder,H[5].lowOrder];
          for (short i = 0; i < 6; i++) {
                  H[i].get(hash, (short) (hashOffset + (short) (i * LONG_LENGTH)));
          }
          break;
      }
      case SHA_512: {
            //return[H[0].highOrder,H[0].lowOrder,H[1].highOrder,H[1].lowOrder,H[2].highOrder,H[2].lowOrder,H[3].highOrder,H[3].lowOrder,H[4].highOrder,H[4].lowOrder,H[5].highOrder,H[5].lowOrder,H[6].highOrder,H[6].lowOrder,H[7].highOrder,H[7].lowOrder];
            for (short i = 0; i < 8; i++) {
                    H[i].get(hash, (short) (hashOffset + (short) (i * LONG_LENGTH)));
            }
            break;
      }
      default:return -1;
  }

  return 0;
}
/**/

void processBlock() {
//
// expand 16 word block into 80 word blocks.
//

  for (short t = 16; t < 80; t++)
  {
//  W[t]=safeAdd(safeAdd(safeAdd(Sigma1(W[(short)(t-2)], OFFSET_result1),W[(short)(t-7)], OFFSET_result2),Sigma0(W[(short)(t-15)], OFFSET_result3), OFFSET_result4),W[(short)(t-16)], W[t]);
    safeAdd(safeAdd(safeAdd(Sigma1(W, (short)((t-2) * 4), OFFSET_result1),W, (short)((short)(t-7) * 4), OFFSET_result2),Sigma0(W, (short)((short) (t-15) * 4), OFFSET_result3), OFFSET_result4),W, (short)((short)(t-16) * 4), W, (short) (t * 4));
  }

//
// set up working variables.
//
  int64Set(int64Variables, OFFSET_a, H[0]);
  int64Set(int64Variables, OFFSET_b, H[1]);
  int64Set(int64Variables, OFFSET_c, H[2]);
  int64Set(int64Variables, OFFSET_d, H[3]);
  int64Set(int64Variables, OFFSET_e, H[4]);
  int64Set(int64Variables, OFFSET_f, H[5]);
  int64Set(int64Variables, OFFSET_g, H[6]);
  int64Set(int64Variables, OFFSET_h, H[7]);

  short resultOffset = 0;
  short t = 0;     // this value is incremented in W[t++]
  for(short i = 0; i < 10; i ++) { // TODO: enable all rounds (should be 10)
    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_h,Sum1(OFFSET_e, OFFSET_result1), OFFSET_h),ch(OFFSET_e, OFFSET_f,OFFSET_g,OFFSET_result3), OFFSET_h),K[t],OFFSET_h),W,(short) (t * 4),OFFSET_h);
    t++;
/*
    resultOffset = safeAdd(OFFSET_h,Sum1(OFFSET_e, OFFSET_result1), OFFSET_h);
    resultOffset = safeAdd(OFFSET_h, ch(OFFSET_e, OFFSET_f,OFFSET_g,OFFSET_result3), OFFSET_h);
    resultOffset = safeAdd(OFFSET_h, K[t],OFFSET_h);
    resultOffset = safeAdd(OFFSET_h,W[t++],OFFSET_h);
    /**/
    resultOffset = safeAdd(OFFSET_d,OFFSET_h,OFFSET_d);
    resultOffset = safeAdd(Sum0(OFFSET_a, OFFSET_result1),maj(OFFSET_a,OFFSET_b,OFFSET_c, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_h,OFFSET_T2,OFFSET_h);

    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_g,Sum1(OFFSET_d, OFFSET_result1), OFFSET_g),ch(OFFSET_d,OFFSET_e,OFFSET_f,OFFSET_result3), OFFSET_g),K[t],OFFSET_g),W,(short) (t * 4),OFFSET_g);
    t++;
    resultOffset = safeAdd(OFFSET_c,OFFSET_g,OFFSET_c);
    resultOffset = safeAdd(Sum0(OFFSET_h, OFFSET_result1),maj(OFFSET_h,OFFSET_a,OFFSET_b, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_g,OFFSET_T2,OFFSET_g);

    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_f,Sum1(OFFSET_c, OFFSET_result1), OFFSET_f),ch(OFFSET_c,OFFSET_d,OFFSET_e,OFFSET_result3), OFFSET_f),K[t],OFFSET_f),W,(short) (t * 4),OFFSET_f);
    t++;
    resultOffset = safeAdd(OFFSET_b,OFFSET_f,OFFSET_b);
    resultOffset = safeAdd(Sum0(OFFSET_g, OFFSET_result1),maj(OFFSET_g,OFFSET_h,OFFSET_a, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_f,OFFSET_T2,OFFSET_f);

    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_e,Sum1(OFFSET_b, OFFSET_result1), OFFSET_e),ch(OFFSET_b,OFFSET_c,OFFSET_d, OFFSET_result3), OFFSET_e),K[t],OFFSET_e),W,(short) (t * 4),OFFSET_e);
    t++;
    resultOffset = safeAdd(OFFSET_a,OFFSET_e,OFFSET_a);
    resultOffset = safeAdd(Sum0(OFFSET_f, OFFSET_result1),maj(OFFSET_f,OFFSET_g,OFFSET_h, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_e,OFFSET_T2,OFFSET_e);

    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_d,Sum1(OFFSET_a, OFFSET_result1), OFFSET_d),ch(OFFSET_a, OFFSET_b,OFFSET_c, OFFSET_result3), OFFSET_d),K[t],OFFSET_d),W,(short) (t * 4),OFFSET_d);
    t++;
    resultOffset = safeAdd(OFFSET_h,OFFSET_d,OFFSET_h);
    resultOffset = safeAdd(Sum0(OFFSET_e, OFFSET_result1),maj(OFFSET_e,OFFSET_f,OFFSET_g, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_d,OFFSET_T2,OFFSET_d);

    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_c,Sum1(OFFSET_h, OFFSET_result1), OFFSET_c),ch(OFFSET_h,OFFSET_a,OFFSET_b, OFFSET_result3), OFFSET_c),K[t],OFFSET_c),W,(short) (t * 4),OFFSET_c);
    t++;
    resultOffset = safeAdd(OFFSET_g,OFFSET_c,OFFSET_g);
    resultOffset = safeAdd(Sum0(OFFSET_d, OFFSET_result1),maj(OFFSET_d,OFFSET_e,OFFSET_f, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_c,OFFSET_T2,OFFSET_c);

    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_b,Sum1(OFFSET_g, OFFSET_result1), OFFSET_b),ch(OFFSET_g,OFFSET_h,OFFSET_a, OFFSET_result3), OFFSET_b),K[t],OFFSET_b),W,(short) (t * 4),OFFSET_b);
    t++;
    resultOffset = safeAdd(OFFSET_f,OFFSET_b,OFFSET_f);
    resultOffset = safeAdd(Sum0(OFFSET_c, OFFSET_result1),maj(OFFSET_c,OFFSET_d,OFFSET_e, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_b,OFFSET_T2,OFFSET_b);

    resultOffset = safeAdd(safeAdd(safeAdd(safeAdd(OFFSET_a,Sum1(OFFSET_f, OFFSET_result1), OFFSET_a),ch(OFFSET_f,OFFSET_g,OFFSET_h, OFFSET_result3), OFFSET_a),K[t],OFFSET_a),W,(short) (t * 4),OFFSET_a);
    t++;
    resultOffset = safeAdd(OFFSET_e,OFFSET_a,OFFSET_e);
    resultOffset = safeAdd(Sum0(OFFSET_b, OFFSET_result1),maj(OFFSET_b,OFFSET_c,OFFSET_d, OFFSET_result2),OFFSET_T2);
    resultOffset = safeAdd(OFFSET_a,OFFSET_T2,OFFSET_a);

  }

  safeAdd(OFFSET_a,H[0],OFFSET_a);
  H[0].set(int64Variables, OFFSET_a);
  safeAdd(OFFSET_b,H[1],OFFSET_b);
  H[1].set(int64Variables, OFFSET_b);
  safeAdd(OFFSET_c,H[2],OFFSET_c);
  H[2].set(int64Variables, OFFSET_c);
  safeAdd(OFFSET_d,H[3],OFFSET_d);
  H[3].set(int64Variables, OFFSET_d);
  safeAdd(OFFSET_e,H[4],OFFSET_e);
  H[4].set(int64Variables, OFFSET_e);
  safeAdd(OFFSET_f,H[5],OFFSET_f);
  H[5].set(int64Variables, OFFSET_f);
  safeAdd(OFFSET_g,H[6],OFFSET_g);
  H[6].set(int64Variables, OFFSET_g);
  safeAdd(OFFSET_h,H[7],OFFSET_h);
  H[7].set(int64Variables, OFFSET_h);

//
// reset the offset and clean out the word buffer.
//
  int64Variables[OFFSET_wOff] = 0;
  for (short i = 0; i < (short) W.length; i++) W[i] = 0;
}

public short rotr(short offset_x, byte n, short offset_result){
//if (rotr_counter < 16000) rotr_counter++;

// right rotation
//int64Set(int64Variables, offset_result, int64Variables, offset_x);
  int64Variables[offset_result] = int64Variables[offset_x];
  int64Variables[(short) (offset_result + 1)] = int64Variables[(short) (offset_x + 1)];
  int64Variables[(short) (offset_result + 2)] = int64Variables[(short) (offset_x + 2)];
  int64Variables[(short) (offset_result + 3)] = int64Variables[(short) (offset_x + 3)];
//    int64Variables[offset_result] = int64Variables[offset_x]; int64Variables[(short) (offset_result + 1)] = int64Variables[(short) (offset_x + 1)]; int64Variables[(short) (offset_result + 2)] = int64Variables[(short) (offset_x + 2)]; int64Variables[(short) (offset_result + 3)] = int64Variables[(short) (offset_x + 3)];
//    result.set(x);

// ROTATE BY 16 UNTIL
  while (n >= 16) {
    int64Variables[(short) (offset_result + 3)] = int64Variables[(short) (offset_result + 2)];
    int64Variables[(short) (offset_result + 2)] = int64Variables[(short) (offset_result + 1)];
    int64Variables[(short) (offset_result + 1)] = int64Variables[(short) (offset_result)];
    int64Variables[offset_result] = (short) 0;
    n = (byte) (n - 16);
  }

// ROTATE REST
  if (n > 0) {
    short mask = 0;
    switch (n) {
      case 1: mask = (short)0x7fff; break;
      case 2: mask = (short)0x3fff; break;
      case 3: mask = (short)0x1fff; break;
      case 4: mask = (short)0x0fff; break;
      case 5: mask = (short)0x07ff; break;
      case 6: mask = (short)0x03ff; break;
      case 7: mask = (short)0x01ff; break;

      case 8: mask = (short)0x00ff; break;
      case 9: mask = (short)0x007f; break;
      case 10: mask = (short)0x003f; break;
      case 11: mask = (short)0x001f; break;
      case 12: mask = (short)0x000f; break;
      case 13: mask = (short)0x0007; break;
      case 14: mask = (short)0x0003; break;
      case 15: mask = (short)0x0001; break;
    }
    int64Variables[(short) (offset_result + 3)] = (short) ((short) (int64Variables[(short) (offset_result + 2)] << (16 - n))  | (short) (int64Variables[(short) (offset_result + 3)] >> n & mask));
    int64Variables[(short) (offset_result + 2)] = (short) ((short) (int64Variables[(short) (offset_result + 1)]  << (16 - n))  | (short) (int64Variables[(short) (offset_result + 2)] >> n & mask));
    int64Variables[(short) (offset_result + 1)] = (short) ((short) (int64Variables[(short) (offset_result)] << (16 - n))  | (short) (int64Variables[(short) (offset_result + 1)] >> n & mask));
    int64Variables[offset_result] = (short) ((int64Variables[(short) (offset_result)] >> n) & mask);
  }
  return offset_result;
}

public short rotl(short offset_x, byte n, short offset_result){
//if (rotl_counter < 16000) rotl_counter++;
// left rotation
//int64Set(int64Variables, offset_result, int64Variables, offset_x);
  int64Variables[offset_result] = int64Variables[offset_x];
  int64Variables[(short) (offset_result + 1)] = int64Variables[(short) (offset_x + 1)];
  int64Variables[(short) (offset_result + 2)] = int64Variables[(short) (offset_x + 2)];
  int64Variables[(short) (offset_result + 3)] = int64Variables[(short) (offset_x + 3)];

// ROTATE BY 16 UNTIL
  while (n >= 16) {
//int64Set(int64Variables, offset_result, int64Variables[(short) (offset_result + 1)], int64Variables[(short) (offset_result + 2)], int64Variables[(short) (offset_result + 3)], (short) 0);
    int64Variables[offset_result] = int64Variables[(short) (offset_result + 1)];
    int64Variables[(short) (offset_result + 1)] = int64Variables[(short) (offset_result + 2)];
    int64Variables[(short) (offset_result + 2)] = int64Variables[(short) (offset_result + 3)];
    int64Variables[(short) (offset_result + 3)] = 0;
    n = (byte) (n - 16);
  }

// ROTATE BY 8 UNTIL
  while (n >= 8) {
//  int64Set(int64Variables, offset_result, (short) ((short) (int64Variables[(short) (offset_result)] << 8) | (short) (int64Variables[(short) (offset_result + 1)] >> 8 & 0xff)), (short) ((short) (int64Variables[(short) (offset_result + 1)] << 8) | (short) (int64Variables[(short) (offset_result + 2)] >> 8 & 0xff)),  (short) ((short) (int64Variables[(short) (offset_result + 2)] << 8) | (short) (int64Variables[(short) (offset_result + 3)] >> 8 & 0xff)),  (short) (int64Variables[(short) (offset_result + 3)] << 8));
    int64Variables[offset_result] = (short) ((short) (int64Variables[(short) (offset_result)] << 8) | (short) (int64Variables[(short) (offset_result + 1)] >> 8 & 0xff));
    int64Variables[(short) (offset_result + 1)] = (short) ((short) (int64Variables[(short) (offset_result + 1)] << 8) | (short) (int64Variables[(short) (offset_result + 2)] >> 8 & 0xff));
    int64Variables[(short) (offset_result + 2)] = (short) ((short) (int64Variables[(short) (offset_result + 2)] << 8) | (short) (int64Variables[(short) (offset_result + 3)] >> 8 & 0xff));
    int64Variables[(short) (offset_result + 3)] = (short) (int64Variables[(short) (offset_result + 3)] << 8);
    n = (byte) (n - 8);
  }

// ROTATE REMAINING
  if (n > 0) {
    short mask = 0;
    switch (n) {
      case 1: mask = (short)0x0001; break;
      case 2: mask = (short)0x0003; break;
      case 3: mask = (short)0x0007; break;
      case 4: mask = (short)0x000f; break;
      case 5: mask = (short)0x001f; break;
      case 6: mask = (short)0x003f; break;
      case 7: mask = (short)0x007f; break;
    }
//int64Set(int64Variables, offset_result, (short) ((short) (int64Variables[(short) (offset_result)] << n) | (short) (int64Variables[(short) (offset_result + 1)] >> (16 - n) & mask)),  (short) ((short) (int64Variables[(short) (offset_result + 1)] << n) | (short) (int64Variables[(short) (offset_result + 2)] >> (16 - n) & mask)),  (short) ((short) (int64Variables[(short) (offset_result + 2)] << n) | (short) (int64Variables[(short) (offset_result + 3)] >> (16 - n) & mask)), (short) (int64Variables[(short) (offset_result + 3)] << n));
    int64Variables[offset_result] = (short) ((short) (int64Variables[(short) (offset_result)] << n) | (short) (int64Variables[(short) (offset_result + 1)] >> (16 - n) & mask));
    int64Variables[(short) (offset_result + 1)] = (short) ((short) (int64Variables[(short) (offset_result + 1)] << n) | (short) (int64Variables[(short) (offset_result + 2)] >> (16 - n) & mask));
    int64Variables[(short) (offset_result + 2)] = (short) ((short) (int64Variables[(short) (offset_result + 2)] << n) | (short) (int64Variables[(short) (offset_result + 3)] >> (16 - n) & mask));
    int64Variables[(short) (offset_result + 3)] = (short) (int64Variables[(short) (offset_result + 3)] << n);
  }
  return offset_result;
}

//  Int_64 ch(Int_64 x, Int_64 y, Int_64 z, Int_64 result){
short ch(short offset_x, short offset_y, short offset_z, short offset_result){
//if (ch_counter < 16000) ch_counter++;
// (x and y) xor ((not x) and z)
  int64Variables[offset_result] = (short) ((short) (int64Variables[(short) (offset_x)] &     int64Variables[(short) (offset_y)]) ^    (short) (~(int64Variables[(short) (offset_x)]) &     int64Variables[(short) (offset_z)]));
  int64Variables[(short) (offset_result + 1)] = (short) ((short) (int64Variables[(short) (offset_x + 1)] & int64Variables[(short) (offset_y + 1)])^ (short) (~(int64Variables[(short) (offset_x + 1)]) & int64Variables[(short) (offset_z + 1)]));
  int64Variables[(short) (offset_result + 2)] = (short) ((short) (int64Variables[(short) (offset_x + 2)] & int64Variables[(short) (offset_y + 2)])^ (short) (~(int64Variables[(short) (offset_x + 2)]) & int64Variables[(short) (offset_z + 2)]));
  int64Variables[(short) (offset_result + 3)] = (short) ((short) (int64Variables[(short) (offset_x + 3)] & int64Variables[(short) (offset_y + 3)])^ (short) (~(int64Variables[(short) (offset_x + 3)]) & int64Variables[(short) (offset_z + 3)]));

  return offset_result;
}

short maj(short offset_x, short offset_y, short offset_z, short offset_result){
//if (maj_counter < 16000) maj_counter++;
// (x and y) xor (x and z) xor (y and z)
//int64Set(int64Variables, offset_result, (short) ((short) (int64Variables[(short) (offset_x)] & int64Variables[(short) (offset_y)]) ^ (short)(int64Variables[(short) (offset_x)] & int64Variables[(short) (offset_z)])^ (short) (int64Variables[(short) (offset_y)] & int64Variables[(short) (offset_z)])), (short) ((short) (int64Variables[(short) (offset_x + 1)] & int64Variables[(short) (offset_y + 1)]) ^ (short)(int64Variables[(short) (offset_x + 1)] & int64Variables[(short) (offset_z + 1)])^ (short) (int64Variables[(short) (offset_y + 1)] & int64Variables[(short) (offset_z + 1)])), (short) ((short) (int64Variables[(short) (offset_x + 2)] & int64Variables[(short) (offset_y + 2)]) ^ (short)(int64Variables[(short) (offset_x + 2)] & int64Variables[(short) (offset_z + 2)])^ (short) (int64Variables[(short) (offset_y + 2)] & int64Variables[(short) (offset_z + 2)])), (short) ((short) (int64Variables[(short) (offset_x + 3)] & int64Variables[(short) (offset_y + 3)]) ^ (short)(int64Variables[(short) (offset_x + 3)] & int64Variables[(short) (offset_z + 3)])^ (short) (int64Variables[(short) (offset_y + 3)] & int64Variables[(short) (offset_z + 3)])));
  int64Variables[offset_result] = (short) ((short) (int64Variables[(short) (offset_x)] & int64Variables[(short) (offset_y)]) ^ (short)(int64Variables[(short) (offset_x)] & int64Variables[(short) (offset_z)])^ (short) (int64Variables[(short) (offset_y)] & int64Variables[(short) (offset_z)]));
  int64Variables[(short) (offset_result + 1)] = (short) ((short) (int64Variables[(short) (offset_x + 1)] & int64Variables[(short) (offset_y + 1)]) ^ (short)(int64Variables[(short) (offset_x + 1)] & int64Variables[(short) (offset_z + 1)])^ (short) (int64Variables[(short) (offset_y + 1)] & int64Variables[(short) (offset_z + 1)]));
  int64Variables[(short) (offset_result + 2)] = (short) ((short) (int64Variables[(short) (offset_x + 2)] & int64Variables[(short) (offset_y + 2)]) ^ (short)(int64Variables[(short) (offset_x + 2)] & int64Variables[(short) (offset_z + 2)])^ (short) (int64Variables[(short) (offset_y + 2)] & int64Variables[(short) (offset_z + 2)]));
  int64Variables[(short) (offset_result + 3)] = (short) ((short) (int64Variables[(short) (offset_x + 3)] & int64Variables[(short) (offset_y + 3)]) ^ (short)(int64Variables[(short) (offset_x + 3)] & int64Variables[(short) (offset_z + 3)])^ (short) (int64Variables[(short) (offset_y + 3)] & int64Variables[(short) (offset_z + 3)]));

  return offset_result;
}


short Sum0(short offset_x, short offset_result)  {
//if (sum0_counter < 16000) sum0_counter++;

//return ((x << 36)|(x >>> 28)) ^ ((x << 30)|(x >>> 34)) ^ ((x << 25)|(x >>> 39));
  rotl(offset_x,(byte)36,OFFSET_rotl_1);
  rotr(offset_x,(byte)28,OFFSET_rotr_1);

  rotl(offset_x,(byte)30,OFFSET_rotl_2);
  rotr(offset_x,(byte)34,OFFSET_rotr_2);

  rotl(offset_x,(byte)25,OFFSET_rotl_3);
  rotr(offset_x,(byte)39,OFFSET_rotr_3);

  int64Variables[offset_result] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotr_1)] | int64Variables[(short) (OFFSET_rotl_1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2)] | int64Variables[(short) (OFFSET_rotr_2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3)] | int64Variables[(short) (OFFSET_rotr_3)])));
  int64Variables[(short) (offset_result + 1)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 1)] | int64Variables[(short) (OFFSET_rotr_1 + 1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 1)] | int64Variables[(short) (OFFSET_rotr_2 + 1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3 + 1)] | int64Variables[(short) (OFFSET_rotr_3 + 1)])));
  int64Variables[(short) (offset_result + 2)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 2)] | int64Variables[(short) (OFFSET_rotr_1 + 2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 2)] | int64Variables[(short) (OFFSET_rotr_2 + 2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3 + 2)] | int64Variables[(short) (OFFSET_rotr_3 + 2)])));
  int64Variables[(short) (offset_result + 3)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 3)] | int64Variables[(short) (OFFSET_rotr_1 + 3)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 3)] | int64Variables[(short) (OFFSET_rotr_2 + 3)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3 + 3)] | int64Variables[(short) (OFFSET_rotr_3 + 3)])));

  return offset_result;
}

short Sum1(short offset_x, short offset_result) {
//if (sum1_counter < 16000) sum1_counter++;

//return ((x << 50)|(x >>> 14)) ^ ((x << 46)|(x >>> 18)) ^ ((x << 23)|(x >>> 41));
  rotl(offset_x,(byte)50,OFFSET_rotl_1);
  rotr(offset_x,(byte)14,OFFSET_rotr_1);

  rotl(offset_x,(byte)46,OFFSET_rotl_2);
  rotr(offset_x,(byte)18,OFFSET_rotr_2);

  rotl(offset_x,(byte)23,OFFSET_rotl_3);
  rotr(offset_x,(byte)41,OFFSET_rotr_3);

  int64Variables[offset_result] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1)] | int64Variables[(short) (OFFSET_rotr_1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2)] | int64Variables[(short) (OFFSET_rotr_2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3)] | int64Variables[(short) (OFFSET_rotr_3)])));
  int64Variables[(short) (offset_result + 1)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 1)] | int64Variables[(short) (OFFSET_rotr_1 + 1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 1)] | int64Variables[(short) (OFFSET_rotr_2 + 1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3 + 1)] | int64Variables[(short) (OFFSET_rotr_3 + 1)])));
  int64Variables[(short) (offset_result + 2)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 2)] | int64Variables[(short) (OFFSET_rotr_1 + 2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 2)] | int64Variables[(short) (OFFSET_rotr_2 + 2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3 + 2)] | int64Variables[(short) (OFFSET_rotr_3 + 2)])));
  int64Variables[(short) (offset_result + 3)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 3)] | int64Variables[(short) (OFFSET_rotr_1 + 3)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 3)] | int64Variables[(short) (OFFSET_rotr_2 + 3)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_3 + 3)] | int64Variables[(short) (OFFSET_rotr_3 + 3)])));

  return offset_result;
}

short Sigma0(Int_64 x, short offset_result) {
  x.get(int64Variables, OFFSET_Sigma0);
  return Sigma0(OFFSET_Sigma0, offset_result);
}
short Sigma0(short[] xArray, short xOffset, short offset_result) {
  for (short i = 0; i < (short) 4; i++) int64Variables[(short) (OFFSET_Sigma0 + i)] = xArray[(short) (xOffset + i)];
  return Sigma0(OFFSET_Sigma0, offset_result);
}
short Sigma0(short offset_x, short offset_result) {
//if (sigma0_counter < 16000) sigma0_counter++;
//return ((x << 63)|(x >>> 1)) ^ ((x << 56)|(x >>> 8)) ^ (x >>> 7);
  rotl(offset_x,(byte)63,OFFSET_rotl_1);
  rotr(offset_x,(byte)1,OFFSET_rotr_1);

  rotl(offset_x,(byte)56,OFFSET_rotl_2);
  rotr(offset_x,(byte)8,OFFSET_rotr_2);

  rotr(offset_x,(byte)7,OFFSET_rotr_3);

  int64Variables[offset_result] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1)] | int64Variables[(short) (OFFSET_rotr_1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2)] | int64Variables[(short) (OFFSET_rotr_2)])) ^ int64Variables[(short) (OFFSET_rotr_3)]);
  int64Variables[(short) (offset_result + 1)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 1)] | int64Variables[(short) (OFFSET_rotr_1 + 1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 1)] | int64Variables[(short) (OFFSET_rotr_2 + 1)])) ^ int64Variables[(short) (OFFSET_rotr_3 + 1)]);
  int64Variables[(short) (offset_result + 2)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 2)] | int64Variables[(short) (OFFSET_rotr_1 + 2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 2)] | int64Variables[(short) (OFFSET_rotr_2 + 2)])) ^ int64Variables[(short) (OFFSET_rotr_3 + 2)]);
  int64Variables[(short) (offset_result + 3)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 3)] | int64Variables[(short) (OFFSET_rotr_1 + 3)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 3)] | int64Variables[(short) (OFFSET_rotr_2 + 3)])) ^ int64Variables[(short) (OFFSET_rotr_3 + 3)]);

  return offset_result;
}

short Sigma1(Int_64 x, short offset_result) {
  x.get(int64Variables, OFFSET_Sigma1);
  return Sigma1(OFFSET_Sigma1, offset_result);
}

short Sigma1(short[] xArray, short xOffset, short offset_result) {
  for (short i = 0; i < (short) 4; i++) int64Variables[(short) (OFFSET_Sigma1 + i)] = xArray[(short) (xOffset + i)];
  return Sigma1(OFFSET_Sigma1, offset_result);
}

short Sigma1(short offset_x, short offset_result) {
//if (sigma1_counter < 16000) sigma1_counter++;

//return ((x << 45)|(x >>> 19)) ^ ((x << 3)|(x >>> 61)) ^ (x >>> 6);
  rotl(offset_x,(byte)45,OFFSET_rotl_1);
  rotr(offset_x,(byte)19,OFFSET_rotr_1);

  rotl(offset_x,(byte)3,OFFSET_rotl_2);
  rotr(offset_x,(byte)61,OFFSET_rotr_2);

  rotr(offset_x,(byte)6,OFFSET_rotr_3);

  int64Variables[offset_result] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1)] | int64Variables[(short) (OFFSET_rotr_1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2)] | int64Variables[(short) (OFFSET_rotr_2)])) ^ int64Variables[(short) (OFFSET_rotr_3)]);
  int64Variables[(short) (offset_result + 1)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 1)] | int64Variables[(short) (OFFSET_rotr_1 + 1)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 1)] | int64Variables[(short) (OFFSET_rotr_2 + 1)])) ^ int64Variables[(short) (OFFSET_rotr_3 + 1)]);
  int64Variables[(short) (offset_result + 2)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 2)] | int64Variables[(short) (OFFSET_rotr_1 + 2)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 2)] | int64Variables[(short) (OFFSET_rotr_2 + 2)])) ^ int64Variables[(short) (OFFSET_rotr_3 + 2)]);
  int64Variables[(short) (offset_result + 3)] = (short) ((short) ((short) (int64Variables[(short) (OFFSET_rotl_1 + 3)] | int64Variables[(short) (OFFSET_rotr_1 + 3)])) ^ (short) ((short) (int64Variables[(short) (OFFSET_rotl_2 + 3)] | int64Variables[(short) (OFFSET_rotr_2 + 3)])) ^ int64Variables[(short) (OFFSET_rotr_3 + 3)]);

  return offset_result;
}

short safeAdd(short offset_x, Int_64 y, short offset_result) {
  y.get(int64Variables, OFFSET_safeAdd);
  return safeAdd(offset_x, OFFSET_safeAdd, offset_result);
}

Int_64 safeAdd(short offset_x, Int_64 y, Int_64 result) {
  y.get(int64Variables, OFFSET_safeAdd);
  safeAdd(offset_x, OFFSET_safeAdd, OFFSET_safeAdd2);
  result.set(int64Variables, OFFSET_safeAdd2);
  return result;
}

short safeAdd(short offset_x, short[] yArray, short yOffset, short[] resultArray, short resultOffset) {
  int64Variables[(short) (OFFSET_safeAdd + 0)] = yArray[(short) (yOffset + 0)];
  int64Variables[(short) (OFFSET_safeAdd + 1)] = yArray[(short) (yOffset + 1)];
  int64Variables[(short) (OFFSET_safeAdd + 2)] = yArray[(short) (yOffset + 2)];
  int64Variables[(short) (OFFSET_safeAdd + 3)] = yArray[(short) (yOffset + 3)];

  safeAdd(offset_x, OFFSET_safeAdd, OFFSET_safeAdd2);

  resultArray[(short) (resultOffset + 0)] = int64Variables[(short) (OFFSET_safeAdd2 + 0)];
  resultArray[(short) (resultOffset + 1)] = int64Variables[(short) (OFFSET_safeAdd2 + 1)];
  resultArray[(short) (resultOffset + 2)] = int64Variables[(short) (OFFSET_safeAdd2 + 2)];
  resultArray[(short) (resultOffset + 3)] = int64Variables[(short) (OFFSET_safeAdd2 + 3)];
  return resultOffset;
}

short safeAdd(short offset_x, short[] yArray, short yOffset, short offset_result) {
  int64Variables[(short) (OFFSET_safeAdd + 0)] = yArray[(short) (yOffset + 0)];
  int64Variables[(short) (OFFSET_safeAdd + 1)] = yArray[(short) (yOffset + 1)];
  int64Variables[(short) (OFFSET_safeAdd + 2)] = yArray[(short) (yOffset + 2)];
  int64Variables[(short) (OFFSET_safeAdd + 3)] = yArray[(short) (yOffset + 3)];
  return safeAdd(offset_x, OFFSET_safeAdd, offset_result);
}


short safeAdd(short offset_x, short offset_y, short offset_result) {
//if (add_counter < 16000) add_counter++;

  boolean bOverflow = false;
  short a1 = (short) ((int64Variables[(short) (offset_x + 3)] & 0xFF) + (int64Variables[(short) (offset_y + 3)] & 0xFF));
  short a2 = (short) ((int64Variables[(short) (offset_x + 3)] >> 8)+(int64Variables[(short) (offset_y + 3)] >> 8)+(a1 >>> 8));
  short lowest = (short) (((a2 & 0xFF)<< 8)|(a1 & 0xFF));

  a1 = (short) ((int64Variables[(short) (offset_x + 2)] & 0xFF) + (int64Variables[(short) (offset_y + 2)] & 0xFF) + (a2 >>> 8));
  a2 = (short) ((int64Variables[(short) (offset_x + 2)] >> 8)+(int64Variables[(short) (offset_y + 2)] >> 8)+(a1 >>> 8));
  short lower = (short) (((a2 & 0xFF)<< 8)|(a1 & 0xFF));
// compensate detected overflow from previous subpart add
  if (bOverflow) {
    lower++;
    bOverflow = false;
    if (lower == 0) bOverflow = true;
  }
// compensate missing sign bit, but detect possible overflow
  if (int64Variables[(short) (offset_x + 3)] < 0) {
    lower++;
    if (lower == 0) bOverflow = true;
  }
  if (int64Variables[(short) (offset_y + 3)] < 0) {
    lower++;
    if (lower == 0) bOverflow = true;
  }


  a1 = (short) ((int64Variables[(short) (offset_x + 1)] & 0xFF) + (int64Variables[(short) (offset_y + 1)] & 0xFF) + (a2 >>> 8));
  a2 = (short) ((int64Variables[(short) (offset_x + 1)] >> 8)+(int64Variables[(short) (offset_y + 1)] >> 8)+(a1 >>> 8));
  short higher = (short) (((a2 & 0xFF)<< 8)|(a1 & 0xFF));
// compensate detected overflow from previous subpart add
  if (bOverflow) {
    higher++;
    bOverflow = false;
    if (higher == 0) bOverflow = true;
  }
// compensate missing sign bit, but detect possible overflow
  if (int64Variables[(short) (offset_x + 2)] < 0) {
    higher++;
    if (higher == 0) bOverflow = true;
  }
  if (int64Variables[(short) (offset_y + 2)] < 0) {
    higher++;
    if (higher == 0) bOverflow = true;
  }

  a1 = (short) ((int64Variables[(short) (offset_x)] & 0xFF) + (int64Variables[(short) (offset_y)] & 0xFF) + (a2 >>> 8));
  a2 = (short) ((int64Variables[(short) (offset_x)] >> 8)+(int64Variables[(short) (offset_y)] >> 8)+(a1 >>> 8));
  short highest = (short) (((a2 & 0xFF)<< 8)|(a1 & 0xFF));

// compensate detected overflow from previous subpart add
  if (bOverflow) {
    highest++;
    bOverflow = false;
    if (highest == 0) bOverflow = true;
  }
// compensate missing sign bit, but detect possible overflow
  if (int64Variables[(short) (offset_x + 1)] < 0) {
    highest++;
    if (highest == 0) bOverflow = true;
  }
  if (int64Variables[(short) (offset_y + 1)] < 0) {
    highest++;
    if (highest == 0) bOverflow = true;
  }

//int64Set(int64Variables, offset_result, highest, higher, lower, lowest);
  int64Variables[offset_result] = highest;
  int64Variables[(short) (offset_result + 1)] = higher;
  int64Variables[(short) (offset_result + 2)] = lower;
  int64Variables[(short) (offset_result + 3)] = lowest;
  /**/
  return offset_result;
}


}