// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// public static int argmax(double[] a) {
//     int imax = 0;
//     for (int i=1; i<a.length; i++) if (a[i] > a[imax]) imax = i;
//     return imax;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// public static double[] interp(double[] x, double[] xp, double[] yp) {
//     // assuming that x and xp are already sorted.
//     // go over x and xp as if we are merging them
//     double[] y = new double[x.length];
//     int i = 0;
//     int ip = 0;
// 
//     // skip x points that are outside the data
//     while (i < x.length && x[i] < xp[0]) i++;
// 
//     while (ip < xp.length && i < x.length) {
//         // skip until we see an xp >= current x
//         while (ip < xp.length && xp[ip] < x[i]) ip++;
//         if (ip >= xp.length) break;
//         if (xp[ip] == x[i]) {
//             y[i] = yp[ip];
//         } else {
//             double dy = yp[ip] - yp[ip-1];
//             double dx = xp[ip] - xp[ip-1];
//             y[i] = yp[ip-1] + dy/dx * (x[i] - xp[ip-1]);
//         }
//         i++;
//     }
//     return y;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// static float max(List<Entry> entries) {
//     float max = Float.MIN_VALUE;
//     for (Entry e : entries) {
//         max = Math.max(max, e.getY());
//     }
//     return max;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// public static double mean(double[] x) {
//     double s = 0;
//     for (double v: x) s += v;
//     return s / x.length;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// static float min(List<Entry> entries) {
//     float min = Float.MAX_VALUE;
//     for (Entry e : entries) {
//         min = Math.min(min, e.getY());
//     }
//     return min;
// }


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.github.mikephil.charting.charts.ScatterChart;
import com.github.mikephil.charting.components.Description;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.ScatterData;
import com.github.mikephil.charting.data.ScatterDataSet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.chromium.latency.walt.Utils.argmax;
import static org.chromium.latency.walt.Utils.interp;
import static org.chromium.latency.walt.Utils.max;
import static org.chromium.latency.walt.Utils.mean;
import static org.chromium.latency.walt.Utils.min;
            phoneEntries.remove(phoneEntries.size() - 1);
        }

        // Adjust waltEntries so min and max is the same as phoneEntries
        float phoneMean = mean(phoneEntries);
        float phoneMax = max(phoneEntries);
        float phoneMin = min(phoneEntries);
        float waltMin = min(waltSmoothEntries);
        float phoneRange = phoneMax - phoneMin;
        float waltRange = max(waltSmoothEntries) - waltMin;
        for (Entry e : waltSmoothEntries) {
            e.setY((e.getY() - waltMin) * (phoneRange / waltRange) + phoneMin - phoneMean);
        }

        // Adjust phoneEntries so mean=0
        for (Entry e : phoneEntries) {
            e.setY(e.getY() - phoneMean);
        }

        double[] shifts = findShifts(phoneEntries, waltSmoothEntries);
        double bestShift = argmax(shifts) / 10d;
        logger.log(String.format("Accelerometer latency: %.1fms", bestShift));

        double[] deltasKernelToCallback = new double[phoneAccelerometerData.size()];
        for (int i = 0; i < deltasKernelToCallback.length; i++) {
            deltasKernelToCallback[i] = phoneAccelerometerData.get(i).callbackTimeMs -
                    phoneAccelerometerData.get(i).kernelTimeMs;
        }

        logger.log(String.format(
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// public static int argmax(double[] a) {
//     int imax = 0;
//     for (int i=1; i<a.length; i++) if (a[i] > a[imax]) imax = i;
//     return imax;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// public static double[] interp(double[] x, double[] xp, double[] yp) {
//     // assuming that x and xp are already sorted.
//     // go over x and xp as if we are merging them
//     double[] y = new double[x.length];
//     int i = 0;
//     int ip = 0;
// 
//     // skip x points that are outside the data
//     while (i < x.length && x[i] < xp[0]) i++;
// 
//     while (ip < xp.length && i < x.length) {
//         // skip until we see an xp >= current x
//         while (ip < xp.length && xp[ip] < x[i]) ip++;
//         if (ip >= xp.length) break;
//         if (xp[ip] == x[i]) {
//             y[i] = yp[ip];
//         } else {
//             double dy = yp[ip] - yp[ip-1];
//             double dx = xp[ip] - xp[ip-1];
//             y[i] = yp[ip-1] + dy/dx * (x[i] - xp[ip-1]);
//         }
//         i++;
//     }
//     return y;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// static float max(List<Entry> entries) {
//     float max = Float.MIN_VALUE;
//     for (Entry e : entries) {
//         max = Math.max(max, e.getY());
//     }
//     return max;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// public static double mean(double[] x) {
//     double s = 0;
//     for (double v: x) s += v;
//     return s / x.length;
// }
// 
// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java
// static float min(List<Entry> entries) {
//     float min = Float.MAX_VALUE;
//     for (Entry e : entries) {
//         min = Math.min(min, e.getY());
//     }
//     return min;
// }


// Path: android/WALT/app/src/main/java/org/chromium/latency/walt/AccelerometerFragment.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.github.mikephil.charting.charts.ScatterChart;
import com.github.mikephil.charting.components.Description;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.ScatterData;
import com.github.mikephil.charting.data.ScatterDataSet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.chromium.latency.walt.Utils.argmax;
import static org.chromium.latency.walt.Utils.interp;
import static org.chromium.latency.walt.Utils.max;
import static org.chromium.latency.walt.Utils.mean;
import static org.chromium.latency.walt.Utils.min;

            phoneEntries.remove(phoneEntries.size() - 1);
        }

        // Adjust waltEntries so min and max is the same as phoneEntries
        float phoneMean = mean(phoneEntries);
        float phoneMax = max(phoneEntries);
        float phoneMin = min(phoneEntries);
        float waltMin = min(waltSmoothEntries);
        float phoneRange = phoneMax - phoneMin;
        float waltRange = max(waltSmoothEntries) - waltMin;
        for (Entry e : waltSmoothEntries) {
            e.setY((e.getY() - waltMin) * (phoneRange / waltRange) + phoneMin - phoneMean);
        }

        // Adjust phoneEntries so mean=0
        for (Entry e : phoneEntries) {
            e.setY(e.getY() - phoneMean);
        }

        double[] shifts = findShifts(phoneEntries, waltSmoothEntries);
        double bestShift = argmax(shifts) / 10d;
        logger.log(String.format("Accelerometer latency: %.1fms", bestShift));

        double[] deltasKernelToCallback = new double[phoneAccelerometerData.size()];
        for (int i = 0; i < deltasKernelToCallback.length; i++) {
            deltasKernelToCallback[i] = phoneAccelerometerData.get(i).callbackTimeMs -
                    phoneAccelerometerData.get(i).kernelTimeMs;
        }

        logger.log(String.format(
                "Mean kernel-to-callback latency: %.1fms", mean(deltasKernelToCallback)));