diff --git a/CHANGELOG.md b/CHANGELOG.md
index f97f08d..259c0b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
+- add rule GCI106 Avoid SQRT in a loop
+
### Changed
- compatibility updates for SonarQube 25.5.0
diff --git a/src/main/java/org/greencodeinitiative/creedengo/python/PythonRuleRepository.java b/src/main/java/org/greencodeinitiative/creedengo/python/PythonRuleRepository.java
index c385979..671d665 100644
--- a/src/main/java/org/greencodeinitiative/creedengo/python/PythonRuleRepository.java
+++ b/src/main/java/org/greencodeinitiative/creedengo/python/PythonRuleRepository.java
@@ -40,7 +40,8 @@ public class PythonRuleRepository implements RulesDefinition, PythonCustomRuleRe
AvoidFullSQLRequest.class,
AvoidListComprehensionInIterations.class,
DetectUnoptimizedImageFormat.class,
- AvoidMultipleIfElseStatementCheck.class
+ AvoidMultipleIfElseStatementCheck.class,
+ AvoidSqrtInLoop.class
);
public static final String LANGUAGE = "py";
diff --git a/src/main/java/org/greencodeinitiative/creedengo/python/checks/AvoidSqrtInLoop.java b/src/main/java/org/greencodeinitiative/creedengo/python/checks/AvoidSqrtInLoop.java
new file mode 100644
index 0000000..3ace5d2
--- /dev/null
+++ b/src/main/java/org/greencodeinitiative/creedengo/python/checks/AvoidSqrtInLoop.java
@@ -0,0 +1,75 @@
+/*
+ * creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
+ * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.greencodeinitiative.creedengo.python.checks;
+
+import org.sonar.check.Rule;
+import org.sonar.plugins.python.api.PythonSubscriptionCheck;
+import org.sonar.plugins.python.api.SubscriptionContext;
+import org.sonar.plugins.python.api.tree.CallExpression;
+import org.sonar.plugins.python.api.tree.Expression;
+import org.sonar.plugins.python.api.tree.QualifiedExpression;
+import org.sonar.plugins.python.api.tree.Tree;
+
+@Rule(key = "GCI106")
+public class AvoidSqrtInLoop extends PythonSubscriptionCheck {
+
+ public static final String DESCRIPTION = "Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.";
+
+ @Override
+ public void initialize(Context context) {
+ context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, this::checkCallExpression);
+ }
+
+ private void checkCallExpression(SubscriptionContext context) {
+ CallExpression callExpression = (CallExpression) context.syntaxNode();
+
+ if (isSqrtCall(callExpression) && hasLoopParent(callExpression)) {
+ context.addIssue(callExpression, DESCRIPTION);
+ }
+ }
+
+ private boolean isSqrtCall(CallExpression callExpression) {
+ Expression callee = callExpression.callee();
+
+ // Check for direct calls to math.sqrt
+ if (callee.is(Tree.Kind.QUALIFIED_EXPR)) {
+ QualifiedExpression qualifiedExpression = (QualifiedExpression) callee;
+ String methodName = qualifiedExpression.name().name();
+
+ if ("sqrt".equals(methodName)) {
+ Expression qualifier = qualifiedExpression.qualifier();
+ if (qualifier != null && qualifier.is(Tree.Kind.NAME)) {
+ String qualifierName = qualifier.firstToken().value();
+ return "math".equals(qualifierName) || "np".equals(qualifierName) || "numpy".equals(qualifierName);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private boolean hasLoopParent(Tree tree) {
+ for (Tree parent = tree.parent(); parent != null; parent = parent.parent()) {
+ Tree.Kind kind = parent.getKind();
+ if (kind == Tree.Kind.FOR_STMT || kind == Tree.Kind.WHILE_STMT) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/greencodeinitiative/creedengo/python/checks/AvoidSqrtInLoopTest.java b/src/test/java/org/greencodeinitiative/creedengo/python/checks/AvoidSqrtInLoopTest.java
new file mode 100644
index 0000000..859cdb2
--- /dev/null
+++ b/src/test/java/org/greencodeinitiative/creedengo/python/checks/AvoidSqrtInLoopTest.java
@@ -0,0 +1,28 @@
+/*
+ * creedengo - Python language - Provides rules to reduce the environmental footprint of your Python programs
+ * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.greencodeinitiative.creedengo.python.checks;
+
+import org.junit.Test;
+import org.sonar.python.checks.utils.PythonCheckVerifier;
+
+public class AvoidSqrtInLoopTest {
+ @Test
+ public void test() {
+ PythonCheckVerifier.verify("src/test/resources/checks/avoidSqrtInLoop.py", new AvoidSqrtInLoop());
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/checks/avoidSqrtInLoop.py b/src/test/resources/checks/avoidSqrtInLoop.py
new file mode 100644
index 0000000..c035f17
--- /dev/null
+++ b/src/test/resources/checks/avoidSqrtInLoop.py
@@ -0,0 +1,75 @@
+import math
+import numpy as np
+
+results = []
+numbers = [1, 2, 3, 4, 5]
+for n in numbers:
+ results.append(math.sqrt(n)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
+
+results = []
+for x in [9, 16, 25]:
+ results.append(np.sqrt(x)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
+
+results = []
+i = 0
+while i < 3:
+ results.append(np.sqrt(i)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
+ i += 1
+
+results = []
+for n in range(10):
+ val = math.sqrt(n) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
+ results.append(val)
+
+class DataProcessor:
+ numbers = np.array([1, 4, 9])
+ sqrt_values = np.sqrt(numbers)
+
+x = math.sqrt(25)
+
+class Analyzer:
+ def __init__(self, data):
+ self.results = np.sqrt(np.array(data))
+
+intermediate = []
+for n in range(3):
+ intermediate.append(n)
+final = np.sqrt(np.array(intermediate))
+
+results = np.sqrt([x for x in range(5)])
+
+def non_compliant_math_sqrt_in_for_loop():
+ numbers = [1, 2, 3, 4, 5]
+ results = []
+ for num in numbers:
+ results.append(math.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
+ return results
+
+def non_compliant_numpy_scalar_sqrt_in_for_loop():
+ numbers = [1, 2, 3, 4, 5]
+ results = []
+ for num in numbers:
+ results.append(np.sqrt(num)) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
+ return results
+
+def non_compliant_math_sqrt_in_while_loop():
+ numbers = [1, 2, 3, 4, 5]
+ results = []
+ i = 0
+ while i < len(numbers):
+ results.append(math.sqrt(numbers[i])) # Noncompliant {{Avoid using scalar sqrt functions in loops. Apply vectorized sqrt operations on arrays directly.}}
+ i += 1
+ return results
+
+def compliant_numpy_vectorized_sqrt():
+ numbers = np.array([1, 2, 3, 4, 5])
+ results = np.sqrt(numbers)
+ return results
+
+def compliant_list_comprehension_with_vectorized_operation():
+ numbers = [1, 2, 3, 4, 5]
+ return np.sqrt(np.array(numbers))
+
+def compliant_math_sqrt_outside_loop():
+ value = 16
+ return math.sqrt(value)
\ No newline at end of file