<commit_before>﻿using System;
using System.Collections.Generic;
using System.Reactive;
using System.Reactive.Subjects;
using SRPCommon.Scripting;
using SRPCommon.UserProperties;
using SRPScripting;
using System.Linq;
using System.Reactive.Linq;

namespace SRPRendering
{
	abstract class UserVariable : IUserProperty
	{
		// IUserProperty interface
		public string Name { get; private set; }
		public bool IsReadOnly { get { return false; } }

		public abstract dynamic GetFunction();

		// IObservable interface
		public abstract IDisposable Subscribe(IObserver<Unit> observer);

		public static UserVariable Create(string name, UserVariableType type, dynamic defaultValue)
		{
			try
			{
				switch (type)
				{
					// Float scalar & vectors.
					case UserVariableType.Float:
						return new UserVariableFloat(name, defaultValue);
					case UserVariableType.Float2:
						return CreateVectorFloat(2, name, defaultValue);
					case UserVariableType.Float3:
						return CreateVectorFloat(3, name, defaultValue);
					case UserVariableType.Float4:
						return CreateVectorFloat(4, name, defaultValue);

					case UserVariableType.Bool:
						return new UserVariableBool(name, defaultValue);

					default:
						throw new ArgumentException("Invalid user variable type.");
				}
			}
			catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
			{
				throw new ScriptException("Incorrect type for user variable default value.", ex);
			}
		}

		private static UserVariable CreateVectorFloat(int numComponents, string name, dynamic defaultValue)
		{
			var components = Enumerable.Range(0, numComponents)
				.Select(i => new UserVariableFloat(i.ToString(), defaultValue[i]))
				.ToArray();
			return new UserVariableVector(name, components);
		}

		protected UserVariable(string name)
		{
			this.Name = name;
		}
	}

	// User variable representing a floating point vector or scalar with a variable number of components.
	class UserVariableFloat : UserVariable, IScalarProperty<float>
	{
		public override dynamic GetFunction()
		{
			Func<float> func = () => value;
			return func;
		}

		public UserVariableFloat(string name, dynamic defaultValue)
			: base(name)
		{
			// Use explicit cast to convert from ints/doubles.
			value = (float)defaultValue;
		}

		public float Value
		{
			get { return value; }
			set
			{
				if (this.value != value)
				{
					this.value = value;
					_subject.OnNext(Unit.Default);
				}
			}
		}

		// IObservable interface
		public override IDisposable Subscribe(IObserver<Unit> observer)
		{
			return _subject.Subscribe(observer);
		}

		// The storage of the actual value.
		private float value;

		private Subject<Unit> _subject = new Subject<Unit>();
	}

	class UserVariableBool : UserVariable, IScalarProperty<bool>
	{
		public override dynamic GetFunction()
		{
			Func<bool> func = () => Value;
			return func;
		}

		public UserVariableBool(string name, bool defaultValue)
			: base(name)
		{
			Value = defaultValue;
		}

		public bool Value
		{
			get { return _value; }
			set
			{
				if (_value != value)
				{
					_value = value;
					_subject.OnNext(Unit.Default);
				}
			}
		}

		// IObservable interface
		public override IDisposable Subscribe(IObserver<Unit> observer)
		{
			return _subject.Subscribe(observer);
		}

		private bool _value;
		private Subject<Unit> _subject = new Subject<Unit>();
	}

	class UserVariableVector : UserVariable, IVectorProperty
	{
		private UserVariable[] components;

		public override dynamic GetFunction()
		{
			// Get the function for each component.
			var subFuncs = components.Select(c => c.GetFunction());

			// Convert result to array so it's subscriptable in script.
			Func<IEnumerable<dynamic>> func = () => subFuncs.Select(s => s()).ToArray();
			return func;
		}

		public UserVariableVector(string name, UserVariable[] components)
			: base(name)
		{<commit_msg>Genericified User Variables to avoid duplication<commit_after>﻿using System;
using System.Collections.Generic;
using System.Reactive;
using System.Reactive.Subjects;
using SRPCommon.Scripting;
using SRPCommon.UserProperties;
using SRPScripting;
using System.Linq;
using System.Reactive.Linq;

namespace SRPRendering
{
	abstract class UserVariable : IUserProperty
	{
		// IUserProperty interface
		public string Name { get; private set; }
		public bool IsReadOnly { get { return false; } }

		public abstract dynamic GetFunction();

		// IObservable interface
		public abstract IDisposable Subscribe(IObserver<Unit> observer);

		public static UserVariable Create(string name, UserVariableType type, dynamic defaultValue)
		{
			try
			{
				switch (type)
				{
					// Scalar types.
					case UserVariableType.Float:
						return new UserVariableScalar<float>(name, defaultValue);
					case UserVariableType.Bool:
						return new UserVariableScalar<bool>(name, defaultValue);

					// Vector types
					case UserVariableType.Float2:
						return CreateVector<float>(2, name, defaultValue);
					case UserVariableType.Float3:
						return CreateVector<float>(3, name, defaultValue);
					case UserVariableType.Float4:
						return CreateVector<float>(4, name, defaultValue);

					default:
						throw new ArgumentException("Invalid user variable type.");
				}
			}
			catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
			{
				throw new ScriptException("Incorrect type for user variable default value.", ex);
			}
		}

		private static UserVariable CreateVector<T>(int numComponents, string name, dynamic defaultValue)
		{
			var components = Enumerable.Range(0, numComponents)
				.Select(i => new UserVariableScalar<T>(i.ToString(), defaultValue[i]))
				.ToArray();
			return new UserVariableVector(name, components);
		}

		protected UserVariable(string name)
		{
			this.Name = name;
		}
	}

	// User variable representing a single value of the given type.
	class UserVariableScalar<T> : UserVariable, IScalarProperty<T>
	{
		public override dynamic GetFunction()
		{
			Func<T> func = () => value;
			return func;
		}

		public UserVariableScalar(string name, dynamic defaultValue)
			: base(name)
		{
			// Use explicit cast to convert from similar types (e.g. ints/doubles -> float).
			value = (T)defaultValue;
		}

		public T Value
		{
			get { return value; }
			set
			{
				if (!EqualityComparer<T>.Default.Equals(this.value, value))
				{
					this.value = value;
					_subject.OnNext(Unit.Default);
				}
			}
		}

		// IObservable interface
		public override IDisposable Subscribe(IObserver<Unit> observer)
		{
			return _subject.Subscribe(observer);
		}

		// The storage of the actual value.
		private T value;

		private Subject<Unit> _subject = new Subject<Unit>();
	}

	// User variable representing a vector of values.
	class UserVariableVector : UserVariable, IVectorProperty
	{
		private UserVariable[] components;

		public override dynamic GetFunction()
		{
			// Get the function for each component.
			var subFuncs = components.Select(c => c.GetFunction());

			// Convert result to array so it's subscriptable in script.
			Func<IEnumerable<dynamic>> func = () => subFuncs.Select(s => s()).ToArray();
			return func;
		}

		public UserVariableVector(string name, UserVariable[] components)
			: base(name)
		{
{'fraction_non_alphanumeric': 0.09640883977900552, 'fraction_numerical': 0.0019337016574585636, 'mean_word_length': 3.6150414276609304, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1852482', 'n_tokens_mistral': 2534, 'n_tokens_neox': 2063, 'n_words': 587}