using JsonSubTypes; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq.Expressions; using System.Linq; using System; using UnityEngine; using MoreLinq; namespace REST_JSON_API { public class ApproximationComparer : EqualityComparer<object> { public static readonly ApproximationComparer Instance = new(); public override bool Equals(object x, object y) { if (x.Equals(y)) return true; if (x.GetType() != y.GetType()) return false; if (x is float fx) return Mathf.Approximately(fx, (float)y); if (x is Vector3 vx) return Math3d.IsApproximatelyEqual(vx, (Vector3)y); return false; } public override int GetHashCode(object obj) { if (obj is float) return typeof(float).GetHashCode(); if (obj is Vector3) return typeof(Vector3).GetHashCode(); return obj.GetHashCode(); } } [JsonConverter(typeof(JsonSubtypes), "kind")] [JsonSubtypes.KnownSubType(typeof(RAW), "RAW")] [JsonSubtypes.KnownSubType(typeof(OMS), "OMS")] [JsonSubtypes.KnownSubType(typeof(OMA), "OMA")] [JsonSubtypes.KnownSubType(typeof(OMV), "VAR")] [JsonSubtypes.KnownSubType(typeof(OML_Member), "OML")] [JsonSubtypes.KnownSubType(typeof(FUN), "FUN")] [JsonSubtypes.KnownSubType(typeof(FUNTYPE), "FUNTYPE")] //[JsonSubtypes.KnownSubType(typeof(OMC<T>), "OMC<" + typeof(T).Name + ">")] [JsonSubtypes.KnownSubType(typeof(OMLIT<bool>), "OMLIT<Boolean>")] [JsonSubtypes.KnownSubType(typeof(OMLIT<byte>), "OMLIT<Byte>")] [JsonSubtypes.KnownSubType(typeof(OMLIT<short>), "OMLIT<Int16>")] [JsonSubtypes.KnownSubType(typeof(OMLIT<int>), "OMLIT<Int32>")] [JsonSubtypes.KnownSubType(typeof(OMLIT<long>), "OMLIT<Int64>")] [JsonSubtypes.KnownSubType(typeof(OMLIT<float>), "OMLIT<Single>")] [JsonSubtypes.KnownSubType(typeof(OMLIT<float>), "OMLIT<Double>")] // we dont use double [JsonSubtypes.KnownSubType(typeof(OMLIT<string>), "OMLIT<String>")] [JsonSubtypes.KnownSubType(typeof(OMLIT<Vector3>), "OMLIT<Vector3>")] [JsonSubtypes.FallBackSubType(typeof(FallbackWrapper))] //[JsonSubtypes.KnownSubType(typeof(FallbackWrapper), "SFunction")] //[JsonSubtypes.KnownSubTypeWithProperty(typeof(FallbackWrapper), "SFunction")] abstract public partial class SOMDoc { public string kind; protected SOMDoc() { kind = this.GetType().Name; } protected internal abstract SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params); public SOMDoc SOMDocType() => SOMDocType(new SOMDoc[0], new FUN.Param[0]); public static SOMDoc SOMDocType(Type type) { SOMDoc[] args = type.IsGenericType ? type.GetGenericArguments().Select(t => SOMDocType(t)).ToArray() : type.HasElementType ? new[] { SOMDocType(type.GetElementType()) } : null; if (type == typeof(Vector3) // Isomoprhismus || TupleExtensions.IsTupleType(type, out _)) // Dictionary does not like Generics type = typeof(Tuple); if (type.HasElementType) // pretend its a List type = typeof(List<>); if (type.IsGenericType) // Dictionary does not like Assembled Generics type = type.GetGenericTypeDefinition(); if (FuncExtensions.IsFuncType(type, out _)) return new FUNTYPE(args[..^1], args[^1]); if (MMTConstants.TYPE_TO_OMS.TryGetValue(type, out string uri)) return args != null ? new OMA(new OMS(uri), args) : new OMS(uri); if (type.IsAnonymousType()) return new OMA( new OMS(MMTConstants.MakeTypeType), type.GetFields() .Select(mem => new OML_Member(mem.Name, SOMDocType(mem.FieldType), null)) .ToArray()); throw new NotImplementedException($"For Type {type}"); } // change to SOMDoc[] args? protected internal abstract Type ToType(Type[] args, (string name, Type type)[] bound_params); public Type ToType() => ToType(new Type[0], new (string name, Type type)[0]); public static bool Equivalent(SOMDoc sd1, SOMDoc sd2) => sd1 != null && sd2 != null && sd1.Equivalent(sd2); public abstract bool Equivalent(SOMDoc sd2); public LambdaExpression GetLambdaExpression() => GetLambdaExpression(new LambdaExpression[0], new LambdaExpression[0], new ParameterExpression[0]); protected internal abstract LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params); public abstract override string ToString(); public abstract SOMDoc MapURIs(Dictionary<string, string> old_to_new); public abstract string[] GetDependentFactIds(); #region MakeMMT_OMS_URItoSOMDoc public static Vector3 MakeVector3(OMA tuple) { if (tuple.arguments is not OMLIT<float>[] xyz || xyz.Length < 3) throw new FormatException("Argument " + nameof(tuple) + "." + nameof(OMA.arguments) + " expected to be: " + nameof(OMLIT<float>) + "[3]"); return new Vector3((float)xyz[0].value, (float)xyz[1].value, (float)xyz[2].value); } public static OMA MakeVector3(Vector3 vec) => MakeTupel(new[] { new OMLIT<float>(vec.x), new OMLIT<float>(vec.y), new OMLIT<float>(vec.z), }); public static OMA MakeTupel(SOMDoc[] args) => new( new OMS(MMTConstants.Tuple), args ); public static OMA MakeShallowList(SOMDoc[] args) => new( new OMS(MMTConstants.ListApplicant), args ); public static OMA MakeDeepList(SOMDoc[] args, SOMDoc SOMDoc_type) { SOMDoc[] end_of_list = new SOMDoc[] { new OMA( new OMS(MMTConstants.ListEnd), new[] { SOMDoc_type, } ), args.Length == 0 ? null : args[^1] }; if (args.Length == 0) end_of_list = end_of_list[..^0]; SOMDoc defines = new OMA( new OMS(MMTConstants.ListLiteral), end_of_list ); for (int i = args.Length - 2; i >= 0; i--) { defines = new OMA( new OMS(MMTConstants.ListLiteral), new[] { defines, args[i], }); } SOMDoc type = new OMA( new OMS(MMTConstants.ListType), new[] { SOMDoc_type } ); return new OMA(type, new[] { defines }); } #endregion MakeMMT_OMS_URItoSOMDoc } public abstract class SOMDocCRTP<T> : SOMDoc where T : SOMDocCRTP<T> { protected SOMDocCRTP() : base() { } public override bool Equivalent(SOMDoc sd2) => this.GetType() == sd2.GetType() && (this as T).EquivalentWrapped(sd2 as T); protected abstract bool EquivalentWrapped(T sd2); public override SOMDoc MapURIs(Dictionary<string, string> old_to_new) => MapURIsWrapped(old_to_new); protected abstract T MapURIsWrapped(Dictionary<string, string> old_to_new); } public class FUNTYPE : SOMDocCRTP<FUNTYPE> { public new string kind = "FUNTYPE"; public SOMDoc[] @params; public SOMDoc ret; [JsonConstructor] public FUNTYPE(SOMDoc[] @params, SOMDoc ret) { this.@params = @params; this.ret = ret; } public override string ToString() => "[" + string.Join(", ", @params.Select(p => p.ToString())) + "] => {" + ret.ToString() + "}"; protected override bool EquivalentWrapped(FUNTYPE sd2) => @params.Length == sd2.@params.Length && @params.Zip(sd2.@params, (a, b) => a.Equivalent(b)) .All(b => b) && ret.Equivalent(sd2.ret); protected override FUNTYPE MapURIsWrapped(Dictionary<string, string> old_to_new) => new(@params.Select(p => p.MapURIs(old_to_new)).ToArray() , ret.MapURIs(old_to_new)); public override string[] GetDependentFactIds() => new string[0]; protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { throw new NotImplementedException(); } protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) { throw new NotImplementedException(); } protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) => FuncExtensions.CreateFuncType(Enumerable.Append(@params.Select(p => p.ToType()), ret.ToType()).ToArray()); } public class FUN : SOMDocCRTP<FUN> { public new string kind = "FUN"; public Param[] @params; public SOMDoc body; [JsonConstructor] public FUN(Param[] @params, SOMDoc body) { this.@params = @params; this.body = body; } public class Param { public string name; [JsonProperty("tp")] public SOMDoc type; [JsonConstructor] public Param(string name, SOMDoc tp) { this.name = name; this.type = tp; } public override string ToString() => name + "(" + type.ToString() + ")"; } public override string ToString() => "[" + string.Join(", ", @params.Select(p => p.ToString())) + "] => {" + body.ToString() + "}"; protected override bool EquivalentWrapped(FUN sd2) => @params.Length == sd2.@params.Length && @params.Zip(sd2.@params, (a, b) => a.name.Equals(b.name) && a.type.Equivalent(b.type)) .All(b => b) && body.Equivalent(sd2.body); protected override FUN MapURIsWrapped(Dictionary<string, string> old_to_new) => new(@params.Select(p => new Param(p.name, p.type.MapURIs(old_to_new))).ToArray() , body.MapURIs(old_to_new)); public override string[] GetDependentFactIds() => body.GetDependentFactIds(); protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { ParameterExpression[] local_param = @params.Select(p => Expression.Parameter(p.type.ToType(), p.name)).ToArray(); LambdaExpression body_lambda = body.GetLambdaExpression( lambda_applicant, lambda_arguments, bound_params.ShallowCloneAppend(local_param) ); return Expression.Lambda( Expression.Lambda( body_lambda.Body, local_param ), bound_params ); } protected internal override SOMDoc SOMDocType(SOMDoc[] args, Param[] bound_params) { Param[] bind_me = bound_params.ShallowCloneAppend(@params); return new FUNTYPE( @params.Select(p => p.type).ToArray(), body.SOMDocType(new SOMDoc[0], bind_me) ); } protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) { (string name, Type)[] bind_me = bound_params .AppendRange(@params.Select(p => (p.name, p.type.ToType()))) .ToArray(); return SOMDocType().ToType(new Type[0], bind_me); } } public class OMV : SOMDocCRTP<OMV> { public new string kind = "VAR"; public string name; [JsonConstructor] public OMV(string name) : base() { this.name = name; } protected override bool EquivalentWrapped(OMV sd2) => this.name == sd2.name; protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) { FUN.Param p = bound_params.FirstOrDefault(param => param.name.Equals(name)) ?? throw new FormatException($"Unable to find {nameof(FUN.Param)} for {nameof(OMV)} with name: {name}"); return p.type; } protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) { (string name, Type type) p = bound_params.FirstOrDefault(param => param.name.Equals(name)); if (p == default) throw new FormatException($"Unable to find {nameof(FUN.Param)} for {nameof(OMV)} with name: {name}"); return p.type; } protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { ParameterExpression v = bound_params.FirstOrDefault(param => param.Name.Equals(name)); if (v == null) { Debug.LogErrorFormat("Unable to find {0} for {1} with name: {2}", nameof(FUN.Param), nameof(OMV), name); return Expression.Lambda(Expression.Empty(), null); } else return Expression.Lambda(v, new[] { v }); } public override string ToString() => "Variable_" + "(" + name + ")"; protected override OMV MapURIsWrapped(Dictionary<string, string> old_to_new) => (OMV)this.MemberwiseClone(); public override string[] GetDependentFactIds() => new string[0]; } public class OMA : SOMDocCRTP<OMA> { public new string kind = "OMA"; public SOMDoc applicant; public SOMDoc[] arguments; [JsonConstructor] public OMA(SOMDoc applicant, SOMDoc[] arguments) : base() { this.applicant = applicant; this.arguments = arguments; } protected override bool EquivalentWrapped(OMA sd2) => Equivalent(this.applicant, sd2.applicant) && this.arguments .Zip(sd2.arguments, (arg1, arg2) => Equivalent(arg1, arg2)) .All(b => b); protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_arguments_paps, LambdaExpression[] lambda_arguments_grands, ParameterExpression[] bound_params) { if (applicant is OMS OMSMake) if (OMSMake.uri == MMTConstants.MakeType || OMSMake.uri == MMTConstants.MakeTypeType) { KeyValuePair<string, LambdaExpression>[] members = arguments .Select(arg => (OML_Member)arg) .Select(rec => new KeyValuePair<string, LambdaExpression>( rec.name, (rec.defines ?? rec.of_type) .GetLambdaExpression(new LambdaExpression[0], new LambdaExpression[0], bound_params))) .ToArray(); return SOMDocToLambdaExpression.MakeType(members, bound_params); } return applicant.GetLambdaExpression( arguments.Select(arg => arg.GetLambdaExpression(new LambdaExpression[0], new LambdaExpression[0], bound_params)).ToArray(), lambda_arguments_paps, bound_params ); } protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) { if (applicant is OMS OMSMake && OMSMake.uri == MMTConstants.GetField) { return (arguments[0] as OMA).arguments .Select(arg => (OML_Member)arg) //RECTYPE? .First(rec => rec.name == ((OML_Member)arguments[1]).name) .defines.SOMDocType(new SOMDoc[0], bound_params); } return applicant.SOMDocType( arguments.Select(a => a.SOMDocType(new SOMDoc[0], bound_params)).ToArray(), bound_params ); } protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) { if (applicant is OMS OMSMake) if (OMSMake.uri == MMTConstants.MakeType || OMSMake.uri == MMTConstants.MakeTypeType) { KeyValuePair<string, Type>[] members = arguments .Select(arg => (OML_Member)arg) .Select(rec => new KeyValuePair<string, Type>( rec.name, rec.of_type.ToType(new Type[0], bound_params))) .ToArray(); return TupleFactory.Create(members).TupleType; } else if (OMSMake.uri == MMTConstants.GetField) { return args[0].GetMember(((OML_Member)arguments[1]).name).First().ReflectedType; } return applicant.ToType( arguments.Select(arg => arg.ToType(new Type[0], bound_params)).ToArray(), bound_params ); } public override string ToString() => applicant.ToString() + "(" + string.Join(", ", arguments.Select(a => a.ToString())) + ")"; protected override OMA MapURIsWrapped(Dictionary<string, string> old_to_new) => new OMA( applicant.MapURIs(old_to_new), arguments.Select(arg => arg.MapURIs(old_to_new)).ToArray() ); public override string[] GetDependentFactIds() => applicant.GetDependentFactIds() .AppendRange(arguments.SelectMany(arg => arg.GetDependentFactIds())) .ToArray(); } public class OMS : SOMDocCRTP<OMS> { public new string kind = "OMS"; public string uri; [JsonConstructor] public OMS(string uri) : base() { this.uri = uri; } protected override bool EquivalentWrapped(OMS sd2) { bool b1 = FactRecorder.AllFacts.TryGetValue(uri, out Fact f1), b2 = FactRecorder.AllFacts.TryGetValue(sd2.uri, out Fact f2); return (b1 == b2 && uri == sd2.uri) || (b1 && b2 && f1.Equivalent(f2)); } protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_arguments_paps, LambdaExpression[] lambda_arguments_grands, ParameterExpression[] bound_params) => SOMDocToLambdaExpression.MakeLambdaExpression(uri, lambda_arguments_paps, lambda_arguments_grands, bound_params); public override string ToString() => uri; protected override OMS MapURIsWrapped(Dictionary<string, string> old_to_new) { if (!old_to_new.TryGetValue(uri, out string new_uri)) new_uri = uri; return new OMS(new_uri); } public override string[] GetDependentFactIds() => FactRecorder.AllFacts.ContainsKey(uri) ? new string[] { uri } : new string[0]; protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) { if (FactRecorder.AllFacts.TryGetValue(uri, out Fact found)) return found.GetType(); if (MMTConstants.HeterogenApplication_TO_TypeOF.TryGetValue(uri, out string s_type)) { Type type = MMTConstants.OMS_TO_TYPE[s_type]; if (type.Equals(typeof(Tuple))) { // authors note: Not a fan, but has to be done somewhwere... if (args.Length == 3 && args.All(t => t == typeof(float))) return typeof(Vector3); type = TupleExtensions.GetGenericTupleType(args.Length); } else if (type.Equals(typeof(Func<>))) return FuncExtensions.CreateFuncType(args); return type .MakeGenericType(args); } if (MMTConstants.HomogenApplication_TO_TypeOF.TryGetValue(uri, out s_type)) return MMTConstants.OMS_TO_TYPE[s_type] .MakeGenericType(args[0]); if (MMTConstants.URI_TO_TypeOF.TryGetValue(uri, out s_type)) return MMTConstants.OMS_TO_TYPE[s_type]; if (MMTConstants.OMS_TO_TYPE.TryGetValue(uri, out Type t_type)) return t_type; throw new NotImplementedException(); } protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) { string _uri = uri; bool use_uri; if (use_uri = MMTConstants.OMS_TO_TYPE.TryGetValue(_uri, out Type tmp_type)) _uri = MMTConstants.TYPE_TO_OMS[tmp_type]; if (FactRecorder.AllFacts.TryGetValue(_uri, out Fact found)) return new OMS(MMTConstants.TYPE_TO_OMS[found.GetType()]); if (MMTConstants.HeterogenApplication_TO_TypeOF.TryGetValue(_uri, out string type)) return new OMA( new OMS(type), args ); if (MMTConstants.HomogenApplication_TO_TypeOF.TryGetValue(_uri, out type)) return new OMA( new OMS(type), new[] { args[0] } ); if (MMTConstants.URI_TO_TypeOF.TryGetValue(_uri, out type)) return new OMS(type); if (use_uri) return SOMDocType(tmp_type); //return new OMS(_uri); throw new NotImplementedException(uri); } } public class RAW : SOMDocCRTP<RAW> { public new string kind = "RAW"; public string xml; [JsonConstructor] public RAW(string xml) : base() { this.xml = xml; } protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) => throw new NotSupportedException(); protected override RAW MapURIsWrapped(Dictionary<string, string> old_to_new) { string copy = xml; foreach (KeyValuePair<string, string> KeyVal in old_to_new) copy = copy.Replace(KeyVal.Key, KeyVal.Value); return new RAW(copy); } public override string[] GetDependentFactIds() => new string[0]; public override string ToString() => xml; protected override bool EquivalentWrapped(RAW sd2) => throw new NotImplementedException(); //xml == sd2.xml; // only exact protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => throw new NotSupportedException(); protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) => throw new NotSupportedException(); } // tp == null && df == null: member access // tp != null : member/type declaration // df != null : member value public class OML_Member : SOMDocCRTP<OML_Member> { public new string kind = "OML"; [JsonProperty("name")] public string name; [JsonProperty("tp")] public SOMDoc of_type; [JsonProperty("df")] public SOMDoc defines; [JsonConstructor] public OML_Member(string name, SOMDoc of_type, SOMDoc defines) { this.name = name; this.of_type = of_type; this.defines = defines; } public override string[] GetDependentFactIds() => defines?.GetDependentFactIds() ?? new string[0]; public override string ToString() { string tp = of_type == null ? string.Empty : $": {of_type}"; string df = defines == null ? string.Empty : $" = {{{defines}}}"; return $".{name}" + tp + df; } protected override bool EquivalentWrapped(OML_Member sd2) => name == sd2.name && ((of_type == null && sd2.of_type == null) || of_type.Equivalent(sd2.of_type)) && ((defines == null && sd2.defines == null) || defines.Equivalent(sd2.defines)); protected override OML_Member MapURIsWrapped(Dictionary<string, string> old_to_new) => new(name, of_type?.MapURIs(old_to_new), defines?.MapURIs(old_to_new)); protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => defines == null && of_type == null ? Expression.Lambda(Expression.Constant(name, typeof(string))) // PropertyInfo? => would need info about sister element in OMA.arguments : throw new NotSupportedException(); //=> new KeyValuePair<string, Expression>(name, value.GetLambdaExpression(lambda_applicant, lambda_arguments, bound_params)) protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) => new OML_Member( name, of_type ?? defines?.SOMDocType(args, bound_params) ?? throw new NotSupportedException(), // would need info about sister element in OMA.arguments null ); protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) => defines?.ToType(args, bound_params) //?? of_type?.ToType(args, bound_params) //is Member Declaration not Member Type ?? throw new NotSupportedException(); // would need info about sister element in OMA.arguments } public class OMLIT<T> : SOMDocCRTP<OMLIT<T>> { public new string kind = "OMLIT<" + typeof(T).Name + ">"; public string type; public T value; [JsonConstructor] public OMLIT(T value, string type = null) : base() { this.value = value; this.type = type ?? MMTConstants.TYPE_TO_OMS[value.GetType()]; } protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) => SOMDocType(typeof(T)); protected override bool EquivalentWrapped(OMLIT<T> sd2) => ApproximationComparer.Instance.Equals(value, sd2); protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => Expression.Lambda(Expression.Constant(value, typeof(T)), null); public override string ToString() => value.ToString(); protected override OMLIT<T> MapURIsWrapped(Dictionary<string, string> old_to_new) => (OMLIT<T>)this.MemberwiseClone(); public override string[] GetDependentFactIds() => new string[0]; protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) => typeof(T); } // temporary fix if Serialzation is broken public class FallbackWrapper : SOMDocCRTP<FallbackWrapper> { public new string kind = "SFunction"; public SOMDoc SFunction; [JsonConstructor] public FallbackWrapper(SOMDoc SFunction) { this.SFunction = SFunction; } public override string ToString() => SFunction.ToString(); protected override bool EquivalentWrapped(FallbackWrapper sd2) => SFunction.Equivalent(sd2); protected override FallbackWrapper MapURIsWrapped(Dictionary<string, string> old_to_new) => new(SFunction.MapURIs(old_to_new)); protected internal override LambdaExpression GetLambdaExpression(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => SFunction.GetLambdaExpression(lambda_applicant, lambda_arguments, bound_params); protected internal override SOMDoc SOMDocType(SOMDoc[] args, FUN.Param[] bound_params) => SFunction.SOMDocType(args, bound_params); protected internal override Type ToType(Type[] args, (string name, Type type)[] bound_params) => SFunction.ToType(args, bound_params); public override string[] GetDependentFactIds() => SFunction.GetDependentFactIds(); } }