Пример #1
0
 private void normalize(final MultivaluedMap<String, String> data)
     throws IllegalArgumentException {
   final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
   final String from = data.getFirst("From");
   if (!from.contains("@")) {
     // https://github.com/Mobicents/RestComm/issues/150 Don't complain in case of URIs in the From
     // header
     data.remove("From");
     try {
       data.putSingle(
           "From",
           phoneNumberUtil.format(phoneNumberUtil.parse(from, "US"), PhoneNumberFormat.E164));
     } catch (final NumberParseException exception) {
       throw new IllegalArgumentException(exception);
     }
   }
   final String to = data.getFirst("To");
   // Only try to normalize phone numbers.
   if (to.startsWith("client")) {
     if (to.split(":").length != 2) {
       throw new IllegalArgumentException(to + " is an invalid client identifier.");
     }
   } else if (!to.contains("@")) {
     data.remove("To");
     try {
       data.putSingle(
           "To", phoneNumberUtil.format(phoneNumberUtil.parse(to, "US"), PhoneNumberFormat.E164));
     } catch (final NumberParseException exception) {
       throw new IllegalArgumentException(exception);
     }
   }
   URI.create(data.getFirst("Url"));
 }
  /*
   * (non-Jsdoc)
   *
   * @see org.talend.dataquality.indicators.impl.IndicatorImpl#handle(java.lang.Object)
   */
  @Override
  public boolean handle(Object data) {
    super.handle(data);
    if (data == null) {
      return false;
    }
    PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
    // }

    // the parameter defualtRegion is null at here, it will get an region code when the data is
    // guaranteed to
    // start with a '+' followed by the country calling code. e.g. "+86 13521588311",
    // "+8613521588311",
    // "+86 1352 1588 311". or else, it will throw Exception and as a invalid Region Code.
    boolean parseSuccess = true;
    String regionCodeForNumber = null;
    try {
      PhoneNumber phhoneNum = phoneUtil.parse(data.toString(), null);
      regionCodeForNumber = phoneUtil.getRegionCodeForNumber(phhoneNum);
    } catch (NumberParseException e) {
      parseSuccess = false;
    }
    Set<String> supportedCountries = phoneUtil.getSupportedRegions();
    if (parseSuccess && supportedCountries.contains(regionCodeForNumber)) {
      this.validRegCount++;
      if (checkMustStoreCurrentRow() || checkMustStoreCurrentRow(drillDownValueCount)) {
        this.mustStoreRow = true;
      }
    }

    return true;
  }
 @Override
 public boolean handle(Object data) {
   super.handle(data);
   if (data == null) {
     return false;
   }
   try {
     PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
     IndicatorParameters indParameters = this.getParameters();
     TextParameters textParameters =
         indParameters == null ? null : indParameters.getTextParameter();
     String country = IndicatorHelper.getCountryCodeByParameter(textParameters);
     PhoneNumber phoneNumeber = phoneUtil.parse(data.toString(), country);
     // It only checks the length of phone numbers. In particular, it doesn't check starting digits
     // of the
     // number.
     if (phoneUtil.isPossibleNumber(phoneNumeber)) {
       this.possiblePhoneCount++;
       if (checkMustStoreCurrentRow() || this.checkMustStoreCurrentRow(drillDownValueCount)) {
         this.mustStoreRow = true;
       }
     }
   } catch (NumberParseException e) {
     return false;
   }
   return true;
 }
Пример #4
0
 protected PhoneNumber getPhoneNumber(final MultivaluedMap<String, String> data) {
   final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
   PhoneNumber phoneNumber = null;
   try {
     phoneNumber = phoneNumberUtil.parse(data.getFirst("PhoneNumber"), "US");
   } catch (final NumberParseException ignored) {
   }
   return phoneNumber;
 }
 private boolean strictGooglePhoneValid(String phoneNumber) {
   try {
     Phonenumber.PhoneNumber usPhoneNumber = phoneUtil.parse(phoneNumber, "US");
     return phoneUtil.isValidNumber(usPhoneNumber);
   } catch (NumberParseException e) {
     log.error("NumberParseException was thrown: " + e.toString());
     return false;
   }
 }
Пример #6
0
 public String remove_plus(String phoneNumber) {
   try {
     if (phoneNumber.charAt(0) == '+') {
       PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
       Phonenumber.PhoneNumber numberProto = phoneUtil.parse(phoneNumber, "");
       return "0" + String.valueOf(numberProto.getNationalNumber());
     } else {
       return phoneNumber;
     }
   } catch (Exception e) {
     return null;
   }
 }
Пример #7
0
 /**
  * Converts the phone number to E164 standard
  *
  * @param phone the phone number to convert
  * @return same number in E164 format
  */
 private String formatToE164(String phone) {
   PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
   Phonenumber.PhoneNumber numberProto = null;
   try {
     numberProto = phoneUtil.parse(phone, getUserCountry());
   } catch (NumberParseException e) {
     System.err.println("NumberParseException was thrown: " + e.toString());
   }
   String formatted = null;
   if (numberProto != null) {
     formatted = phoneUtil.format(numberProto, PhoneNumberUtil.PhoneNumberFormat.E164);
   }
   return (formatted != null ? formatted : phone.replaceAll("[^0-9+]+", ""));
 }
Пример #8
0
  public static String isMobile(String mobile) {
    String phone = null;
    try {
      PhoneNumber pn = phoneUtil.parse(mobile, "CN");

      if (phoneUtil.isValidNumber(pn)) {
        phone = phoneUtil.format(pn, PhoneNumberFormat.E164).replace("+86", "");
        LOGGER.debug("phone util parse into cn : " + phone);
      }
    } catch (Exception e) {
      LOGGER.error("check mobile error! ", e);
    }

    return phone;
  }
  private boolean checkInput() {
    mPhoneNumber = null;
    String phoneStr = null;

    // check name first
    mName = mNameText.getText().toString().trim();
    if (mName.length() == 0) {
      error(R.string.title_no_name, R.string.msg_no_name);
      return false;
    }

    PhoneNumberUtil util = PhoneNumberUtil.getInstance();
    CountryCode cc = (CountryCode) mCountryCode.getSelectedItem();
    if (!BuildConfig.DEBUG) {
      PhoneNumber phone;
      try {
        phone = util.parse(mPhone.getText().toString(), cc.regionCode);
        if (!util.isValidNumberForRegion(phone, cc.regionCode)) {
          throw new NumberParseException(
              ErrorType.INVALID_COUNTRY_CODE, "invalid number for region " + cc.regionCode);
        }
      } catch (NumberParseException e1) {
        error(R.string.title_invalid_number, R.string.msg_invalid_number);
        return false;
      }

      // check phone number format
      if (phone != null) {
        phoneStr = util.format(phone, PhoneNumberFormat.E164);
        if (!PhoneNumberUtils.isWellFormedSmsAddress(phoneStr)) {
          Log.i(TAG, "not a well formed SMS address");
        }
      }
    } else {
      phoneStr = String.format(Locale.US, "+%d%s", cc.countryCode, mPhone.getText().toString());
    }

    // phone is null - invalid number
    if (phoneStr == null) {
      Toast.makeText(this, R.string.warn_invalid_number, Toast.LENGTH_SHORT).show();
      return false;
    }

    Log.v(TAG, "Using phone number to register: " + phoneStr);
    mPhoneNumber = phoneStr;
    return true;
  }
Пример #10
0
  public static String isPhoneNumber(String mobile) {
    String phone = null;
    try {
      LOGGER.debug("test phone number : " + mobile);
      PhoneNumber pn = phoneUtil.parse(mobile, "CN");

      if (phoneUtil.isValidNumber(pn)) {
        phone = phoneUtil.format(pn, PhoneNumberFormat.E164).replace("+86", "");
        LOGGER.debug("phone util parse into cn : " + phone);
      }
    } catch (NumberParseException e) {
      LOGGER.error("parse valid number! ", e);
    }

    if (phone == null) phone = mobile.replace("+", "");

    return phone;
  }
Пример #11
0
  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    log.log(Level.INFO, "RegisterServlet.doPOST() : START RegisterServlet.doPOST()");

    resp.setContentType("text/plain");

    GeatteRegisterRequestInfo reqInfo =
        GeatteRegisterRequestInfo.processRequest(req, resp, getServletContext());
    if (reqInfo == null) {
      log.severe("RegisterServlet.doPOST() : can not load RequestInfo!!");
      return;
    }

    // Because the deviceRegistrationId isn't static, we use a static
    // identifier for the device. (Can be null in older clients)
    String deviceId = reqInfo.getParameter(Config.DEVICE_ID_PARAM);

    if (deviceId == null) {
      resp.setStatus(400);
      resp.getWriter().println(ERROR_STATUS + "(Must specify " + Config.DEVICE_ID_PARAM + ")");
      log.severe(
          "RegisterServlet.doPOST() : Missing device id, " + Config.DEVICE_ID_PARAM + " is null");
      return;
    } else {
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : user sent a " + Config.DEVICE_ID_PARAM + " = " + deviceId);
    }

    String phoneNumber = reqInfo.getParameter(Config.DEV_PHONE_NUMBER_PARAM);

    if (phoneNumber == null) {
      resp.setStatus(400);
      resp.getWriter()
          .println(ERROR_STATUS + "(Must specify " + Config.DEV_PHONE_NUMBER_PARAM + ")");
      log.severe(
          "RegisterServlet.doPOST() : Missing phone number, "
              + Config.DEV_PHONE_NUMBER_PARAM
              + " is null");
      return;
    } else {
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : user sent a "
              + Config.DEV_PHONE_NUMBER_PARAM
              + " = "
              + phoneNumber);
    }

    String phoneCountryIso = reqInfo.getParameter(Config.DEV_PHONE_COUNTRY_ISO_PARAM);

    if (phoneCountryIso == null) {
      // default is us
      phoneCountryIso = "us";
      log.severe(
          "RegisterServlet.doPOST() : Missing phone country iso, "
              + Config.DEV_PHONE_COUNTRY_ISO_PARAM
              + " is null");
      return;
    } else {
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : user sent a "
              + Config.DEV_PHONE_COUNTRY_ISO_PARAM
              + " = "
              + phoneCountryIso);
    }

    PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
    PhoneNumber numberProto = null;
    try {
      numberProto = phoneUtil.parse(phoneNumber, phoneCountryIso);
    } catch (NumberParseException npe) {
      log.log(Level.WARNING, "RegisterServlet.doPOST(): NumberParseException was thrown: ", npe);
    }

    if (numberProto != null && phoneUtil.isValidNumber(numberProto)) {
      phoneNumber = phoneUtil.format(numberProto, PhoneNumberFormat.E164);
    } else {
      log.log(
          Level.WARNING,
          "RegisterServlet.doPOST() : Invalid phone number so use passed-in number, "
              + Config.DEV_PHONE_NUMBER_PARAM
              + " = "
              + phoneNumber
              + ", countryIso = "
              + phoneCountryIso);
    }

    if (reqInfo.deviceRegistrationID == null) {
      resp.setStatus(400);
      resp.getWriter().println(ERROR_STATUS + "(Must specify " + Config.DEV_REG_ID_PARAM + ")");
      log.severe(
          "RegisterServlet.doPOST() : Missing registration id, reqInfo.deviceRegistrationID is null");
      return;
    } else {
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : reqInfo.deviceRegistrationID = "
              + reqInfo.deviceRegistrationID);
    }

    String deviceName = reqInfo.getParameter(Config.DEVICE_NAME_PARAM);

    if (deviceName == null) {
      deviceName = "Phone";
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : use default "
              + Config.DEVICE_NAME_PARAM
              + " = "
              + deviceName);
    } else {
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : user sent a "
              + Config.DEVICE_NAME_PARAM
              + " = "
              + deviceName);
    }
    // TODO: generate the device name by adding a number suffix for multiple
    // devices of same type. Change android app to send model/type.

    String deviceType = reqInfo.getParameter(Config.DEVICE_TYPE_PARAM);

    if (deviceType == null) {
      deviceType = DeviceInfo.TYPE_AC2DM;
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : use default "
              + Config.DEVICE_TYPE_PARAM
              + " = "
              + deviceType);
    } else {
      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : user sent a "
              + Config.DEVICE_TYPE_PARAM
              + " = "
              + deviceType);
    }

    // Context-shared PMF.
    PersistenceManager pm = DBHelper.getPMF(getServletContext()).getPersistenceManager();

    try {
      List<DeviceInfo> registrations = reqInfo.devices;

      if (registrations.size() > MAX_DEVICES) {
        log.log(
            Level.INFO,
            "RegisterServlet.doPOST() : user has too many devices, registrations.size = "
                + registrations.size());
        // we could return an error - but user can't handle it yet.
        // we can't let it grow out of bounds.
        // TODO: we should also define a 'ping' message and
        // expire/remove
        // unused registrations
        DeviceInfo oldest = registrations.get(0);
        if (oldest.getRegistrationTimestamp() == null) {
          log.log(
              Level.INFO,
              "RegisterServlet.doPOST() : user has too many devices, trying to remove old one = "
                  + oldest.getDeviceRegistrationID());
          pm.deletePersistent(oldest);
        } else {
          long oldestTime = oldest.getRegistrationTimestamp().getTime();
          for (int i = 1; i < registrations.size(); i++) {
            if (registrations.get(i).getRegistrationTimestamp().getTime() < oldestTime) {
              oldest = registrations.get(i);
              oldestTime = oldest.getRegistrationTimestamp().getTime();
            }
          }
          log.log(
              Level.INFO,
              "RegisterServlet.doPOST() : user has too many devices, trying to remove old one = "
                  + oldest.getDeviceRegistrationID());
          pm.deletePersistent(oldest);
        }
      }

      // Get device if it already exists, else create
      // String suffix = (deviceId != null ? "#" +
      // Long.toHexString(Math.abs(deviceId.hashCode())) : "");
      // Key key = KeyFactory.createKey(DeviceInfo.class.getSimpleName(),
      // reqInfo.userName + suffix);
      Key key = KeyFactory.createKey(DeviceInfo.class.getSimpleName(), deviceId);

      DeviceInfo device = null;
      try {
        device = pm.getObjectById(DeviceInfo.class, key);
      } catch (JDOObjectNotFoundException e) {
      }
      if (device == null) {
        log.log(
            Level.INFO,
            "RegisterServlet.doPOST() : can not find a DeviceInfo by key = "
                + key
                + ", create a new one");

        // clean devices for same phone number, only one device allowed for one phone
        try {
          List<DeviceInfo> devicesForSameNumber =
              DeviceInfo.getDeviceInfoForNumber(pm, phoneNumber, phoneCountryIso);
          for (DeviceInfo deviceSameNumber : devicesForSameNumber) {
            pm.deletePersistent(deviceSameNumber);
          }
        } catch (JDOObjectNotFoundException e) {
        }

        device = new DeviceInfo(key, reqInfo.deviceRegistrationID);
        device.setType(deviceType);
      } else {
        log.log(
            Level.INFO,
            "RegisterServlet.doPOST() : find a DeviceInfo by key = "
                + key
                + ", update deviceRegistrationID to "
                + reqInfo.deviceRegistrationID);

        // update registration id
        device.setDeviceRegistrationID(reqInfo.deviceRegistrationID);
        device.setRegistrationTimestamp(new Date());
      }

      device.setPhoneNumber(phoneNumber); // update phoneNumber
      device.setCountryCode(phoneCountryIso); // update country code
      device.setUserEmail(reqInfo.getUserEmail()); // update user email
      device.setDeviceName(deviceName); // update display name
      // TODO: only need to write if something changed, for chrome nothing
      // changes, we just create a new channel
      pm.makePersistent(device);

      log.log(
          Level.INFO,
          "RegisterServlet.doPOST() : Registered device userEmail = "
              + reqInfo.getUserEmail()
              + ", deviceType = "
              + deviceType
              + ", deviceRegistrationID = "
              + reqInfo.deviceRegistrationID
              + ", key = "
              + key);

      // if type is IOS, register to Urban
      if (device.getType().equalsIgnoreCase(DeviceInfo.TYPE_IOS)) {
        doRegisterIOSToUrban(device);
        resp.getWriter().println(OK_STATUS);
      } else if (device.getType().equalsIgnoreCase(DeviceInfo.TYPE_ANDROID)
          || device.getType().equalsIgnoreCase(DeviceInfo.TYPE_AC2DM)) {
        resp.getWriter().println(OK_STATUS);
      } else {
        resp.getWriter().println(ERROR_STATUS + "(Wrong " + Config.DEVICE_TYPE_PARAM + ")");
      }

      log.log(Level.INFO, "RegisterServlet.doPOST() : END RegisterServlet.doPOST()");
    } catch (Exception e) {
      resp.setStatus(500);
      resp.getWriter().println(ERROR_STATUS + " (Error registering device)");
      log.log(Level.WARNING, "RegisterServlet.doPOST() : Error registering device.", e);
    } finally {
      pm.close();
    }
  }
Пример #12
0
  public static ArrayList<PhoneBookContact> loadPhoneBook(Context context, String isoCountry) {
    if (DISABLE_PHONE_BOOK) {
      return new ArrayList<PhoneBookContact>();
    }

    Log.d(TAG, "Loading phone book");
    long start = SystemClock.uptimeMillis();

    HashSet<Long> addedPhones = new HashSet<Long>();
    HashSet<String> addedEmails = new HashSet<String>();
    ArrayList<PhoneBookContact> records = new ArrayList<PhoneBookContact>();
    HashMap<Long, PhoneBookContact> recordsMap = new HashMap<Long, PhoneBookContact>();

    ContentResolver cr = context.getContentResolver();
    if (cr == null) {
      return new ArrayList<PhoneBookContact>();
    }

    // Loading records
    // TODO: Better logic for duplicate phones
    // Check have permission for this
    if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS)
            != PackageManager.PERMISSION_GRANTED
        && ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {
      Log.d("Permissions", "contacts - no permission :c");
      return new ArrayList<PhoneBookContact>();
    }
    Cursor cur =
        cr.query(
            ContactsContract.Contacts.CONTENT_URI,
            new String[] {ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME},
            null,
            null,
            ContactsContract.Contacts.SORT_KEY_PRIMARY);
    if (cur == null) {
      return new ArrayList<PhoneBookContact>();
    }
    int idIndex = cur.getColumnIndex(ContactsContract.Contacts._ID);
    int nameIndex = cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);

    int index = 0;
    while (cur.moveToNext()) {
      if (index++ == READ_ITEM_DELAY_BATCH) {
        if (READ_ITEM_DELAY > 0) {
          try {
            Thread.sleep(READ_ITEM_DELAY);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }

      long id = cur.getLong(idIndex);
      String name = cur.getString(nameIndex);
      if (name == null || name.trim().length() == 0) continue;

      PhoneBookContact record =
          new PhoneBookContact(
              id, name.trim(), new ArrayList<PhoneBookPhone>(), new ArrayList<PhoneBookEmail>());
      records.add(record);
      recordsMap.put(id, record);
    }
    cur.close();
    cur = null;

    // Loading phones
    cur =
        cr.query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            new String[] {
              ContactsContract.CommonDataKinds.Phone._ID,
              ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
              ContactsContract.CommonDataKinds.Phone.NUMBER
            },
            null,
            null,
            ContactsContract.CommonDataKinds.Phone._ID + " desc");
    if (cur == null) {
      return new ArrayList<PhoneBookContact>();
    }

    final int idContactIndex =
        cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
    final int idPhoneIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID);
    final int idNumberIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);

    while (cur.moveToNext()) {
      if (index++ == READ_ITEM_DELAY_BATCH) {
        if (READ_ITEM_DELAY > 0) {
          try {
            Thread.sleep(READ_ITEM_DELAY);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }

      long contactId = cur.getLong(idContactIndex);
      long phoneId = cur.getLong(idPhoneIndex);
      String rawPhone = cur.getString(idNumberIndex);
      PhoneBookContact record = recordsMap.get(contactId);
      if (record == null) {
        continue;
      }

      try {
        final Phonenumber.PhoneNumber phonenumber = phoneUtil.parse(rawPhone, isoCountry);
        rawPhone = phonenumber.getCountryCode() + "" + phonenumber.getNationalNumber();
      } catch (final NumberParseException e) {
        rawPhone = rawPhone.replaceAll("[^\\d]", "");
      }
      long phone = -1;
      try {
        phone = Long.parseLong(rawPhone);
      } catch (Exception e) {
        // Logger.d(TAG, "Can't parse number", e);
        continue;
      }

      if (addedPhones.contains(phone)) {
        continue;
      }
      addedPhones.add(phone);

      record.getPhones().add(new PhoneBookPhone(phoneId, phone));
    }
    cur.close();
    cur = null;

    // Loading emails
    cur =
        cr.query(
            ContactsContract.CommonDataKinds.Email.CONTENT_URI,
            new String[] {
              ContactsContract.CommonDataKinds.Email._ID,
              ContactsContract.CommonDataKinds.Email.CONTACT_ID,
              ContactsContract.CommonDataKinds.Email.ADDRESS
            },
            null,
            null,
            ContactsContract.CommonDataKinds.Email._ID + " desc");

    if (cur == null) {
      return new ArrayList<PhoneBookContact>();
    }

    final int idEmailContactIndex =
        cur.getColumnIndex(ContactsContract.CommonDataKinds.Email.CONTACT_ID);
    final int idEmailIdIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Email._ID);
    final int idEmailIndex = cur.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);

    while (cur.moveToNext()) {
      if (index++ == READ_ITEM_DELAY_BATCH) {
        if (READ_ITEM_DELAY > 0) {
          try {
            Thread.sleep(READ_ITEM_DELAY);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }

      long contactId = cur.getLong(idEmailContactIndex);
      long emailId = cur.getLong(idEmailIdIndex);
      String rawEmail = cur.getString(idEmailIndex);
      PhoneBookContact record = recordsMap.get(contactId);
      if (record == null) {
        continue;
      }

      if (rawEmail == null) {
        continue;
      }
      rawEmail = rawEmail.toLowerCase();

      if (addedEmails.contains(rawEmail)) {
        continue;
      }
      addedEmails.add(rawEmail);

      record.getEmails().add(new PhoneBookEmail(emailId, rawEmail));
    }
    cur.close();

    // Filtering records without contacts
    ArrayList<PhoneBookContact> res = new ArrayList<PhoneBookContact>();
    for (PhoneBookContact rec : records) {
      if (rec.getPhones().size() > 0 || rec.getEmails().size() > 0) {
        res.add(rec);
      }
    }
    Log.d(
        TAG,
        "Phone book loaded in "
            + (SystemClock.uptimeMillis() - start)
            + " ms in "
            + (index)
            + " iterations");
    return res;
  }