@@ -51,12 +51,24 @@ defmodule Zixir.Engine do
5151 Automatically falls back to Elixir implementation if NIFs are not available.
5252 """
5353 def run ( op , args ) do
54+ run_with_fallback ( op , args , _fallback_attempted = false )
55+ end
56+
57+ defp run_with_fallback ( op , args , fallback_attempted ) do
5458 try do
5559 do_run ( op , args )
5660 rescue
5761 _e in ErlangError ->
58- Logger . debug ( "NIF not available for #{ op } , using Elixir fallback" )
59- run_fallback ( op , args )
62+ if fallback_attempted do
63+ # Already tried fallback, don't retry to avoid infinite loop
64+ Logger . error ( "NIF not available for #{ op } and fallback also failed" )
65+ raise ArgumentError , "Engine operation #{ inspect ( op ) } failed: NIF not available and fallback failed"
66+ else
67+ Logger . debug ( "NIF not available for #{ op } , using Elixir fallback" )
68+ # The _safe functions in Zixir.Engine.Math already handle both NIF and pure Elixir
69+ # implementations internally, so we can retry once with the same do_run
70+ run_with_fallback ( op , args , true )
71+ end
6072 end
6173 end
6274
@@ -182,123 +194,6 @@ defmodule Zixir.Engine do
182194 raise ArgumentError , "unknown engine op: #{ inspect ( op ) } "
183195 end
184196
185- # Fallback implementations (same as do_run but always use _safe versions)
186- defp run_fallback ( :list_sum , args ) , do: Zixir.Engine.Math . list_sum_safe ( List . first ( args ) || [ ] )
187- defp run_fallback ( :list_product , args ) , do: Zixir.Engine.Math . list_product_safe ( List . first ( args ) || [ ] )
188- defp run_fallback ( :list_mean , args ) , do: Zixir.Engine.Math . list_mean_safe ( List . first ( args ) || [ ] )
189- defp run_fallback ( :list_min , args ) , do: Zixir.Engine.Math . list_min_safe ( List . first ( args ) || [ ] )
190- defp run_fallback ( :list_max , args ) , do: Zixir.Engine.Math . list_max_safe ( List . first ( args ) || [ ] )
191- defp run_fallback ( :list_variance , args ) , do: Zixir.Engine.Math . list_variance_safe ( List . first ( args ) || [ ] )
192- defp run_fallback ( :list_std , args ) , do: Zixir.Engine.Math . list_std_safe ( List . first ( args ) || [ ] )
193-
194- defp run_fallback ( :dot_product , args ) do
195- a = Enum . at ( args , 0 ) || [ ]
196- b = Enum . at ( args , 1 ) || [ ]
197- Zixir.Engine.Math . dot_product_safe ( a , b )
198- end
199-
200- defp run_fallback ( :vec_add , args ) do
201- a = Enum . at ( args , 0 ) || [ ]
202- b = Enum . at ( args , 1 ) || [ ]
203- Zixir.Engine.Math . vec_add_safe ( a , b )
204- end
205-
206- defp run_fallback ( :vec_sub , args ) do
207- a = Enum . at ( args , 0 ) || [ ]
208- b = Enum . at ( args , 1 ) || [ ]
209- Zixir.Engine.Math . vec_sub_safe ( a , b )
210- end
211-
212- defp run_fallback ( :vec_mul , args ) do
213- a = Enum . at ( args , 0 ) || [ ]
214- b = Enum . at ( args , 1 ) || [ ]
215- Zixir.Engine.Math . vec_mul_safe ( a , b )
216- end
217-
218- defp run_fallback ( :vec_div , args ) do
219- a = Enum . at ( args , 0 ) || [ ]
220- b = Enum . at ( args , 1 ) || [ ]
221- Zixir.Engine.Math . vec_div_safe ( a , b )
222- end
223-
224- defp run_fallback ( :vec_scale , args ) do
225- array = Enum . at ( args , 0 ) || [ ]
226- scalar = Enum . at ( args , 1 ) || 1.0
227- Zixir.Engine.Math . vec_scale_safe ( array , scalar )
228- end
229-
230- defp run_fallback ( :map_add , args ) do
231- array = Enum . at ( args , 0 ) || [ ]
232- value = Enum . at ( args , 1 ) || 0.0
233- Zixir.Engine.Math . map_add_safe ( array , value )
234- end
235-
236- defp run_fallback ( :map_mul , args ) do
237- array = Enum . at ( args , 0 ) || [ ]
238- value = Enum . at ( args , 1 ) || 1.0
239- Zixir.Engine.Math . map_mul_safe ( array , value )
240- end
241-
242- defp run_fallback ( :filter_gt , args ) do
243- array = Enum . at ( args , 0 ) || [ ]
244- threshold = Enum . at ( args , 1 ) || 0.0
245- Zixir.Engine.Math . filter_gt_safe ( array , threshold )
246- end
247-
248- defp run_fallback ( :sort_asc , args ) , do: Zixir.Engine.Math . sort_asc_safe ( List . first ( args ) || [ ] )
249-
250- defp run_fallback ( :find_index , args ) do
251- array = Enum . at ( args , 0 ) || [ ]
252- value = Enum . at ( args , 1 ) || 0.0
253- Zixir.Engine.Math . find_index_safe ( array , value )
254- end
255-
256- defp run_fallback ( :count_value , args ) do
257- array = Enum . at ( args , 0 ) || [ ]
258- value = Enum . at ( args , 1 ) || 0.0
259- Zixir.Engine.Math . count_value_safe ( array , value )
260- end
261-
262- defp run_fallback ( :mat_mul , args ) do
263- a = Enum . at ( args , 0 ) || [ ]
264- b = Enum . at ( args , 1 ) || [ ]
265- a_rows = Enum . at ( args , 2 ) || 1
266- a_cols = Enum . at ( args , 3 ) || 1
267- b_cols = Enum . at ( args , 4 ) || 1
268- Zixir.Engine.Math . mat_mul_safe ( a , b , a_rows , a_cols , b_cols )
269- end
270-
271- defp run_fallback ( :mat_transpose , args ) do
272- matrix = Enum . at ( args , 0 ) || [ ]
273- rows = Enum . at ( args , 1 ) || 1
274- cols = Enum . at ( args , 2 ) || 1
275- Zixir.Engine.Math . mat_transpose_safe ( matrix , rows , cols )
276- end
277-
278- defp run_fallback ( :string_count , args ) , do: Zixir.Engine.Math . string_count_safe ( List . first ( args ) || "" )
279-
280- defp run_fallback ( :string_find , args ) do
281- haystack = Enum . at ( args , 0 ) || ""
282- needle = Enum . at ( args , 1 ) || ""
283- Zixir.Engine.Math . string_find_safe ( haystack , needle )
284- end
285-
286- defp run_fallback ( :string_starts_with , args ) do
287- string = Enum . at ( args , 0 ) || ""
288- prefix = Enum . at ( args , 1 ) || ""
289- Zixir.Engine.Math . string_starts_with_safe ( string , prefix )
290- end
291-
292- defp run_fallback ( :string_ends_with , args ) do
293- string = Enum . at ( args , 0 ) || ""
294- suffix = Enum . at ( args , 1 ) || ""
295- Zixir.Engine.Math . string_ends_with_safe ( string , suffix )
296- end
297-
298- defp run_fallback ( op , _args ) do
299- raise ArgumentError , "unknown engine op: #{ inspect ( op ) } "
300- end
301-
302197 @ doc """
303198 Check if engine NIFs are available.
304199 """
0 commit comments