From 1289ef5c0bd36a700124c136dda6c34ed7f9954a Mon Sep 17 00:00:00 2001 From: brharrington Date: Fri, 10 Nov 2023 15:39:37 -0600 Subject: [PATCH] core: add support for basic trig functions (#1589) These can be useful for defining synthetic data when demoing operations. --- .../netflix/atlas/core/model/MathExpr.scala | 7 +++ .../atlas/core/model/MathVocabulary.scala | 45 ++++++++++++++++++- .../core/model/ModelExtractorsSuite.scala | 4 +- .../netflix/atlas/webapi/ExprApiSuite.scala | 2 +- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathExpr.scala b/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathExpr.scala index 103e740d0..f50288d8f 100644 --- a/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathExpr.scala +++ b/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathExpr.scala @@ -378,6 +378,13 @@ object MathExpr { def apply(v: Double): Double = -v } + case class Sine(expr: TimeSeriesExpr) extends UnaryMathExpr { + + def name: String = "sin" + + def apply(v: Double): Double = math.sin(v) + } + case class Sqrt(expr: TimeSeriesExpr) extends UnaryMathExpr { def name: String = "sqrt" diff --git a/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathVocabulary.scala b/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathVocabulary.scala index f5ace0736..9de6c10d1 100644 --- a/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathVocabulary.scala +++ b/atlas-core/src/main/scala/com/netflix/atlas/core/model/MathVocabulary.scala @@ -40,6 +40,7 @@ object MathVocabulary extends Vocabulary { As, GroupBy, Const, + Pi, Random, SeededRandom, Time, @@ -51,6 +52,7 @@ object MathVocabulary extends Vocabulary { ClampMax, Abs, Negate, + Sine, Sqrt, PerStep, Add, @@ -231,7 +233,12 @@ object MathVocabulary extends Vocabulary { ), List("name,playback.startLatency,:eq") ), - Macro("median", List("(", "50", ")", ":percentiles"), List("name,requestLatency,:eq")) + Macro("median", List("(", "50", ")", ":percentiles"), List("name,requestLatency,:eq")), + Macro("cos", List(":pi", "2", ":div", ":swap", ":sub", ":sin")), + Macro("tan", List(":dup", ":sin", ":swap", ":cos", ":div")), + Macro("cot", List(":dup", ":cos", ":swap", ":sin", ":div")), + Macro("sec", List("1", ":swap", ":cos", ":div")), + Macro("csc", List("1", ":swap", ":sin", ":div")) ) case object As extends SimpleWord { @@ -324,6 +331,28 @@ object MathVocabulary extends Vocabulary { override def examples: List[String] = List("42") } + case object Pi extends SimpleWord { + + override def name: String = "pi" + + protected def matcher: PartialFunction[List[Any], Boolean] = { + case _ => true + } + + protected def executor: PartialFunction[List[Any], List[Any]] = { + case stack => MathExpr.Constant(Math.PI) :: stack + } + + override def summary: String = + """ + |Generates a line where each datapoint has the value of the mathematical constant π. + """.stripMargin.trim + + override def signature: String = " -- TimeSeriesExpr" + + override def examples: List[String] = Nil + } + case object Random extends SimpleWord { override def name: String = "random" @@ -727,6 +756,19 @@ object MathVocabulary extends Vocabulary { """.stripMargin.trim } + case object Sine extends UnaryWord { + + override def name: String = "sin" + + def newInstance(t: TimeSeriesExpr): TimeSeriesExpr = MathExpr.Sine(t) + + override def summary: String = + """ + |Compute a new time series where each interval has the sine of the value from the + |input time series. + """.stripMargin.trim + } + case object Sqrt extends UnaryWord { override def name: String = "sqrt" @@ -1197,4 +1239,5 @@ object MathVocabulary extends Vocabulary { "name,requestLatency,:eq,(,25,50,90,)" ) } + } diff --git a/atlas-core/src/test/scala/com/netflix/atlas/core/model/ModelExtractorsSuite.scala b/atlas-core/src/test/scala/com/netflix/atlas/core/model/ModelExtractorsSuite.scala index 63bae2f33..3595d1ae2 100644 --- a/atlas-core/src/test/scala/com/netflix/atlas/core/model/ModelExtractorsSuite.scala +++ b/atlas-core/src/test/scala/com/netflix/atlas/core/model/ModelExtractorsSuite.scala @@ -34,7 +34,7 @@ class ModelExtractorsSuite extends FunSuite { completionTest("name", 8) completionTest("name,sps", 22) - completionTest("name,sps,:eq", 20) - completionTest("name,sps,:eq,app,foo,:eq", 41) + completionTest("name,sps,:eq", 21) + completionTest("name,sps,:eq,app,foo,:eq", 42) completionTest("name,sps,:eq,app,foo,:eq,:and,(,asg,)", 12) } diff --git a/atlas-webapi/src/test/scala/com/netflix/atlas/webapi/ExprApiSuite.scala b/atlas-webapi/src/test/scala/com/netflix/atlas/webapi/ExprApiSuite.scala index be74ac084..37b8b6e9c 100644 --- a/atlas-webapi/src/test/scala/com/netflix/atlas/webapi/ExprApiSuite.scala +++ b/atlas-webapi/src/test/scala/com/netflix/atlas/webapi/ExprApiSuite.scala @@ -163,7 +163,7 @@ class ExprApiSuite extends MUnitRouteSuite { testGet("/api/v1/expr/complete?q=name,sps,:eq,(,nf.cluster,)") { assertEquals(response.status, StatusCodes.OK) val data = Json.decode[List[ExprApiSuite.Candidate]](responseAs[String]).map(_.name) - assertEquals(data, List("by", "by", "cg", "offset", "palette")) + assertEquals(data, List("by", "by", "pi", "cg", "offset", "palette")) } // TODO: Right now these fail. As a future improvement suggestions should be possible within