[perf] Don't display LSE where it doesn't apply.

Also fix bug so LSE gets reported when it does apply.

Bug: skia:10392
Change-Id: I221a1dd6f29fd3db7225c16c9c56e32ad8dc363f
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/301636
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
Commit-Queue: Joe Gregorio <jcgregorio@google.com>
diff --git a/perf/go/stepfit/stepfit.go b/perf/go/stepfit/stepfit.go
index 66752c7..3575b43 100644
--- a/perf/go/stepfit/stepfit.go
+++ b/perf/go/stepfit/stepfit.go
@@ -33,7 +33,9 @@
 //
 // Used in ClusterSummary.
 type StepFit struct {
-	// LeastSquares is the Least Squares error for a step function curve fit to the trace.
+	// LeastSquares is the Least Squares error for a step function curve fit to
+	// the trace. Will be set to InvalidLeastSquaresError if LSE isn't
+	// calculated for a given algorithm.
 	LeastSquares float32 `json:"least_squares"`
 
 	// TurningPoint is the index where the Step Function changes value.
@@ -58,6 +60,10 @@
 	Status StepFitStatus `json:"status"`
 }
 
+// InvalidLeastSquaresError signals that the value of StepFit.LeastSquares is
+// invalid, i.e. it is not calculated for the given algorithm.
+const InvalidLeastSquaresError = -1
+
 // NewStepFit creates an properly initialized StepFit struct.
 func NewStepFit() *StepFit {
 	return &StepFit{
@@ -92,7 +98,8 @@
 		trace = trace[0 : len(trace)-1]
 	}
 
-	var lse float32
+	var lse float32 = InvalidLeastSquaresError
+
 	var regression float32
 	stepSize := float32(-1.0)
 	i := len(trace) / 2
@@ -104,7 +111,7 @@
 	if stepDetection == types.OriginalStep {
 		// This is the original recipe step detection as described at
 		// https://bitworking.org/news/2014/11/detecting-benchmark-regressions
-		lse := float32(math.MaxFloat32)
+		lse = float32(math.MaxFloat32)
 		if y0 != y1 {
 			d := vec32.SSE(trace[:i], y0) + vec32.SSE(trace[i:], y1)
 			if d < lse {
diff --git a/perf/go/stepfit/stepfit_test.go b/perf/go/stepfit/stepfit_test.go
index 8c901ff..f7d0e65 100644
--- a/perf/go/stepfit/stepfit_test.go
+++ b/perf/go/stepfit/stepfit_test.go
@@ -41,75 +41,76 @@
 func TestStepFit_NoStep(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 2, StepSize: -1, Status: UNINTERESTING, Regression: -2.7105057e-19},
+		&StepFit{TurningPoint: 2, StepSize: -1, Status: UNINTERESTING, Regression: -2.7105057e-19,
+			LeastSquares: 3.6893486e+18},
 		GetStepFitAtMid([]float32{1, 1, 1, 1, 1}, minStdDev, 50, types.OriginalStep))
 }
 
 func TestStepFit_Absolute_NoStep(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 2, StepSize: 0, Status: UNINTERESTING, Regression: 0},
+		&StepFit{TurningPoint: 2, StepSize: 0, Status: UNINTERESTING, Regression: 0, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 2, 1, 2, x}, minStdDev, 1.0, types.AbsoluteStep))
 }
 
 func TestStepFit_Absolute_StepExactMatch(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 2, StepSize: -1, Status: HIGH, Regression: -1},
+		&StepFit{TurningPoint: 2, StepSize: -1, Status: HIGH, Regression: -1, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 1, 2, 2, x}, minStdDev, 1.0, types.AbsoluteStep))
 }
 
 func TestStepFit_Absolute_StepTooSmall(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 2, StepSize: -0.5, Status: UNINTERESTING, Regression: -0.5},
+		&StepFit{TurningPoint: 2, StepSize: -0.5, Status: UNINTERESTING, Regression: -0.5, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 1, 1.5, 1.5, x}, minStdDev, 1.0, types.AbsoluteStep))
 }
 
 func TestStepFit_Percent_NoStep(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 2, StepSize: 0, Status: UNINTERESTING, Regression: 0},
+		&StepFit{TurningPoint: 2, StepSize: 0, Status: UNINTERESTING, Regression: 0, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 2, 1, 2, x}, minStdDev, 1.0, types.PercentStep))
 }
 
 func TestStepFit_Percent_StepExactMatch(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 1, StepSize: -1, Status: HIGH, Regression: -1},
+		&StepFit{TurningPoint: 1, StepSize: -1, Status: HIGH, Regression: -1, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 2, x}, minStdDev, 1.0, types.PercentStep))
 }
 
 func TestStepFit_Percent_StepTooSmall(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 1, StepSize: -0.5, Status: UNINTERESTING, Regression: -0.5},
+		&StepFit{TurningPoint: 1, StepSize: -0.5, Status: UNINTERESTING, Regression: -0.5, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 1.5, x}, minStdDev, 1.0, types.PercentStep))
 }
 
 func TestStepFit_Cohen_NoStep(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 3, StepSize: -0.1999998, Status: UNINTERESTING, Regression: -0.1999998},
+		&StepFit{TurningPoint: 3, StepSize: -0.1999998, Status: UNINTERESTING, Regression: -0.1999998, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 1.1, 0.9, 1.02, 1.12, 0.92, x}, minStdDev, 1.0, types.CohenStep))
 }
 
 func TestStepFit_Cohen_Step(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 3, StepSize: -0.1999998, Status: HIGH, Regression: -0.1999998},
+		&StepFit{TurningPoint: 3, StepSize: -0.1999998, Status: HIGH, Regression: -0.1999998, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 1.1, 0.9, 1.02, 1.12, 0.92, x}, minStdDev, 0.1, types.CohenStep))
 }
 
 func TestStepFit_Cohen_StepWithZeroStandardDeviation(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{TurningPoint: 2, StepSize: -10, Status: HIGH, Regression: -10},
+		&StepFit{TurningPoint: 2, StepSize: -10, Status: HIGH, Regression: -10, LeastSquares: InvalidLeastSquaresError},
 		GetStepFitAtMid([]float32{1, 1, 2, 2, x}, minStdDev, 0.2, types.CohenStep))
 }
 func TestStepFit_Cohen_StepWithLargeStandardDeviation(t *testing.T) {
 	unittest.SmallTest(t)
 	assert.Equal(t,
-		&StepFit{LeastSquares: 0, TurningPoint: 2, StepSize: -2.828427, Regression: -2.828427, Status: "High"},
+		&StepFit{LeastSquares: InvalidLeastSquaresError, TurningPoint: 2, StepSize: -2.828427, Regression: -2.828427, Status: "High"},
 		GetStepFitAtMid([]float32{1, 2, 3, 4, x}, minStdDev, 0.2, types.CohenStep))
 }
diff --git a/perf/modules/cluster-summary2-sk/cluster-summary2-sk-demo.ts b/perf/modules/cluster-summary2-sk/cluster-summary2-sk-demo.ts
index 8d81f7d..9a951f2 100644
--- a/perf/modules/cluster-summary2-sk/cluster-summary2-sk-demo.ts
+++ b/perf/modules/cluster-summary2-sk/cluster-summary2-sk-demo.ts
@@ -109,9 +109,10 @@
 );
 cluster!.full_summary = fullSummary;
 
-const summary2 = JSON.parse(JSON.stringify(fullSummary));
-summary2.summary.step_fit.status = 'High';
-summary2.summary.step_fit.regression = 201;
+const summary2: FullSummary = JSON.parse(JSON.stringify(fullSummary));
+summary2.summary.step_fit!.status = 'High';
+summary2.summary.step_fit!.regression = 201;
+summary2.summary.step_fit!.least_squares = -1; // Should be hidden.
 const nostatus = document.querySelector<ClusterSummary2Sk>(
   'cluster-summary2-sk.nostatus'
 );
diff --git a/perf/modules/cluster-summary2-sk/cluster-summary2-sk.ts b/perf/modules/cluster-summary2-sk/cluster-summary2-sk.ts
index 212ea01..5842dad 100644
--- a/perf/modules/cluster-summary2-sk/cluster-summary2-sk.ts
+++ b/perf/modules/cluster-summary2-sk/cluster-summary2-sk.ts
@@ -88,16 +88,18 @@
 export class ClusterSummary2Sk extends ElementSk {
   private static template = (ele: ClusterSummary2Sk) => html`
     <div class="regression ${ele.statusClass()}">
-      Regression: <span>${trunc(ele.summary!.step_fit!.regression)}</span>
+      Regression:
+      <span>${trunc(ele.summary!.step_fit!.regression)}</span>
     </div>
     <div class="stats">
-      <div class="labelled">Cluster Size: <span>${ele.summary.num}</span></div>
       <div class="labelled">
-        Least Squares Error:
-        <span> ${trunc(ele.summary!.step_fit!.least_squares)} </span>
+        Cluster Size:
+        <span>${ele.summary.num}</span>
       </div>
+      ${ClusterSummary2Sk.leastSquares(ele)}
       <div class="labelled">
-        Step Size: <span>${trunc(ele.summary!.step_fit!.step_size)} </span>
+        Step Size:
+        <span>${trunc(ele.summary!.step_fit!.step_size)}</span>
       </div>
     </div>
     <plot-simple-sk
@@ -106,8 +108,7 @@
       height="250"
       specialevents
       @trace_selected=${ele.traceSelected}
-    >
-    </plot-simple-sk>
+    ></plot-simple-sk>
     <div id="status" class=${ele.hiddenClass()}>
       <p class="disabledMessage">You must be logged in to change the status.</p>
       <triage2-sk
@@ -115,8 +116,7 @@
         @change=${(e: CustomEvent<Status>) => {
           ele.triageStatus.status = e.detail;
         }}
-      >
-      </triage2-sk>
+      ></triage2-sk>
       <input
         type="text"
         .value=${ele.triageStatus.message}
@@ -125,14 +125,14 @@
         }}
         label="Message"
       />
-      <button class="action" @click=${ele.update}> Update </button>
+      <button class="action" @click=${ele.update}>Update</button>
     </div>
     <commit-detail-panel-sk id="commits" selectable></commit-detail-panel-sk>
     <div class="actions">
       <button id="shortcut" @click=${ele.openShortcut}>
         View on dashboard
       </button>
-      <button @click=${ele.toggleWordCloud}> Word Cloud </button>
+      <button @click=${ele.toggleWordCloud}>Word Cloud</button>
       <a id="permalink" class=${ele.hiddenClass()} href=${ele.permaLink()}>
         Permlink
       </a>
@@ -143,6 +143,18 @@
     </collapse-sk>
   `;
 
+  private static leastSquares = (ele: ClusterSummary2Sk) => {
+    if (ele.summary!.step_fit!.least_squares >= 0) {
+      return html`
+        <div class="labelled">
+          Least Squares Error:
+          <span>${trunc(ele.summary!.step_fit!.least_squares)}</span>
+        </div>
+      `;
+    }
+    return html``;
+  };
+
   private summary: ClusterSummary;
   private triageStatus: TriageStatus;
   private wordCloud: CollapseSk | null = null;