public void onDraw(Canvas c) {
   synchronized (this) {
     for (Stain s : toDrawList) {
       s.onDraw(c);
     }
   }
   if (drawSponge) {
     sponge.onDraw(c);
   }
 }
 public boolean onTouch(int x, int y) {
   if (drawSponge) {
     List<Stain> toDelete = new ArrayList<>();
     sponge.setXY(x, y);
     for (Stain s : toDrawList) {
       if (s.clean(x, y)) {
         toDelete.add(s);
       }
     }
     synchronized (this) {
       toDrawList.removeAll(toDelete);
     }
     if (toDrawList.size() == 0) {
       drawSponge = false;
       return true;
     }
   }
   return false;
 }
  public static int LYRA2(
      byte[] output_key,
      byte[] user_password,
      byte[] salt,
      long time_cost,
      int memory_matrix_rows,
      int memory_matrix_columns) {

    // ============================= Basic variables ============================//
    int row = 2; // index of row to be processed
    int prev = 1; // index of prev (last row ever computed/modified)
    int rowa =
        0; // index of row* (a previous row, deterministically picked during Setup and randomly
           // picked while Wandering)
    int tau; // Time Loop iterator
    int step = 1; // Visitation step (used during Setup and Wandering phases)
    int window = 2; // Visitation window (used to define which rows can be revisited during Setup)
    int gap = 1; // Modifier to the step, assuming the values 1 or -1
    int i; // auxiliary iteration counter
    // ==========================================================================/

    // ========== Initializing the Memory Matrix and pointers to it =============//
    // Tries to allocate enough space for the whole memory matrix

    final int ROW_LEN_INT64 = Sponge.BLOCK_LEN_INT64 * memory_matrix_columns;

    long[] wholeMatrix = new long[memory_matrix_rows * ROW_LEN_INT64];
    padding(
        output_key.length,
        time_cost,
        user_password,
        salt,
        memory_matrix_rows,
        memory_matrix_columns,
        wholeMatrix);
    // ======================= Initializing the Sponge State ====================//
    // Sponge state: 16 long, BLOCK_LEN_INT64 words of them for the bitrate (b) and the remainder
    // for the capacity (c)
    long[] state = new long[16];
    Sponge.initState(state);
    // ==========================================================================/

    // ================================ Setup Phase =============================//
    // Absorbing salt, password and basil: this is the only place in which the block length is
    // hard-coded to 512 bits
    int dest_pos = Sponge.ANY_ARRAY_START;
    final int blocks_input =
        ((user_password.length + salt.length + 6 * Sponge.LONG_SIZE_IN_BYTES)
                / Sponge.BLOCK_LEN_BLAKE2_SAFE_BYTES)
            + 1;
    for (i = 0; i < blocks_input; i++) {
      Sponge.absorbBlockBlake2Safe(
          state,
          wholeMatrix,
          dest_pos); // absorbs each block of pad(user_password || salt || basil)
      dest_pos +=
          Sponge
              .BLOCK_LEN_BLAKE2_SAFE_INT64; // goes to next block of pad(user_password || salt ||
                                            // basil)
    }

    // Initializes M[0] and M[1]
    Sponge.reducedSqueezeRow0(
        state,
        wholeMatrix,
        memory_matrix_columns); // The locally copied password is most likely overwritten here

    final int first_column = 0;
    Sponge.reducedDuplexRow1(
        state,
        wholeMatrix,
        to1dIndex(rowa, first_column, ROW_LEN_INT64),
        to1dIndex(prev, first_column, ROW_LEN_INT64),
        memory_matrix_columns);

    do {
      // M[row] = rand; //M[row*] = M[row*] XOR rotW(rand)
      Sponge.reducedDuplexRowSetup(
          state,
          wholeMatrix,
          to1dIndex(prev, first_column, ROW_LEN_INT64),
          to1dIndex(rowa, first_column, ROW_LEN_INT64),
          to1dIndex(row, first_column, ROW_LEN_INT64),
          memory_matrix_columns);

      // updates the value of row* (deterministically picked during Setup))
      rowa = (rowa + step) & (window - 1);
      // update prev: it now points to the last row ever computed
      prev = row;
      // updates row: goes to the next row to be computed
      row++;

      // Checks if all rows in the window where visited.
      if (rowa == 0) {
        step = window + gap; // changes the step: approximately doubles its value
        window *= 2; // doubles the size of the re-visitation window
        gap = -gap; // inverts the modifier to the step
      }

    } while (row < memory_matrix_rows);
    // ==========================================================================/

    // ============================ Wandering Phase =============================//
    row = 0; // Resets the visitation to the first row of the memory matrix
    for (tau = 1; tau <= time_cost; tau++) {
      // Step is approximately half the number of all rows of the memory matrix for an odd tau;
      // otherwise, it is -1
      BigInteger bigstep;
      if (tau % 2 == 0) {
        bigstep = new BigInteger("18446744073709551615");
      } else {
        bigstep = new BigInteger(Integer.toString(memory_matrix_rows / 2 - 1));
      }
      do {
        // Selects a pseudorandom index row*
        // ------------------------------------------------------------------------------------------
        // rowa = ((unsigned int)state[0]) & (memory_matrix_rows-1);	//(USE THIS IF
        // memory_matrix_rows IS A POWER OF 2)
        // rowa = Math.abs((byte) state[0] % memory_matrix_rows);
        BigInteger unsigned_state = long2unsigned(state[0]);
        BigInteger modulo =
            unsigned_state.mod(new BigInteger(Integer.toString(memory_matrix_rows)));
        rowa = modulo.intValue();
        // ------------------------------------------------------------------------------------------
        // Performs a reduced-round duplexing operation over M[row*] XOR M[prev], updating both
        // M[row*] and M[row]
        Sponge.reducedDuplexRow(
            state,
            wholeMatrix,
            to1dIndex(prev, first_column, ROW_LEN_INT64),
            to1dIndex(rowa, first_column, ROW_LEN_INT64),
            to1dIndex(row, first_column, ROW_LEN_INT64),
            memory_matrix_columns);

        // update prev: it now points to the last row ever computed
        prev = row;

        // updates row: goes to the next row to be computed
        // ------------------------------------------------------------------------------------------
        // row = (row + step) & (memory_matrix_rows-1);	//(USE THIS IF memory_matrix_rows IS A POWER
        // OF 2)
        // (row + step) % memory_matrix_rows; //(USE THIS FOR THE "GENERIC" CASE)
        row =
            (int)
                bigstep
                    .add(new BigInteger(Integer.toString(row)))
                    .mod(new BigInteger(Integer.toString(memory_matrix_rows)))
                    .longValue();
        // ------------------------------------------------------------------------------------------

      } while (row != 0);
    }
    // ==========================================================================/

    // ============================ Wrap-up Phase ===============================//
    // Absorbs the last block of the memory matrix
    Sponge.absorbBlock(state, wholeMatrix, to1dIndex(rowa, first_column, ROW_LEN_INT64));

    // Squeezes the key
    Sponge.squeeze(state, output_key, output_key.length);
    // ==========================================================================/

    return 0;
  }