Skip to content
Snippets Groups Projects
SOMDocs.cs 29.39 KiB
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();
    }
}