# 45757ff3e742fd0e8b8e35aff31e4f97d71334cc5bbbbde2b161ca2980530644 Elan 2.0.0-alpha4 valid

function bestFitLine(points as List<of Point>) returns (Float, Float)
  let sumX be points.reduce(0.0, lambda s as Float, p as Point => s + p.x)
  let sumXsq be points.reduce(0.0, lambda s as Float, p as Point => s + pow(p.x, 2))
  let sumY be points.reduce(0.0, lambda s as Float, p as Point => s + p.y)
  let sumXY be points.reduce(0.0, lambda s as Float, p as Point => s + p.x*p.y)
  let n be points.length()
  let a be (sumY*sumXsq - sumX*sumXY)/(n*sumXsq - sumX*sumX)
  let b be (n*sumXY - sumX*sumY)/(n*sumXsq - sumX*sumX)
  return (a, b)
end function

class Point
  property x as Float

  property y as Float

  constructor(x as Float, y as Float)
    set this.x to x
    set this.y to y
  end constructor

  function toString() returns String
    return $"Point {this.x}, {this.y}"
  end function

end class

function newPoint(x as Float, y as Float) returns Point
  return new Point(x, y)
end function

test test_bestFit
  let l1 be [newPoint(0.71, 1.12), newPoint(3.56, 5.36), newPoint(7.83, 9.04)]
  let a_b be bestFitLine(l1)
  let a be a_b.item_0
  let b be a_b.item_1
  assert a.round(3) is 0.766
  assert b.round(3) is 1.093
end test

test test_bestFit_empty
  let l1 be new List<of Point>()
  let a_b be bestFitLine(l1)
  # NaN means 'Not A Number"
  assert a_b.toString() is "(NaN, NaN)"
end test
