<commit_before>import { FSharpRef, Record, Union } from "./Types.js";
import { combineHashCodes, equalArraysWith, IEquatable, stringHash } from "./Util.js";

export type FieldInfo = [string, TypeInfo];
export type PropertyInfo = FieldInfo;
export type ParameterInfo = FieldInfo;

export type Constructor = new (...args: any[]) => any;

export class CaseInfo {
  constructor(
    public declaringType: TypeInfo,
    public tag: number,
    public name: string,
    public fields?: FieldInfo[]) {
  }
}

export type EnumCase = [string, number];

export class MethodInfo {
  constructor(
    public name: string,
    public parameters: ParameterInfo[],
    public returnType: TypeInfo,
  ) {
  }
}

export class TypeInfo implements IEquatable<TypeInfo> {
  constructor(
    public fullname: string,
    public generics?: TypeInfo[],
    public construct?: Constructor,
    public parent?: TypeInfo,
    public fields?: () => FieldInfo[],
    public cases?: () => CaseInfo[],
    public enumCases?: EnumCase[]) {
  }
  public toString() {
    return fullName(this);
  }
  public GetHashCode() {
    return getHashCode(this);
  }
  public Equals(other: TypeInfo) {
    return equals(this, other);
  }
}

export function getGenerics(t: TypeInfo): TypeInfo[] {
  return t.generics != null ? t.generics : [];
}

export function getHashCode(t: TypeInfo) {
  const fullnameHash = stringHash(t.fullname);
  const genHashes: number[] = getGenerics(t).map(getHashCode);
  return combineHashCodes([fullnameHash, ...genHashes]);
}

export function equals(t1: TypeInfo, t2: TypeInfo): boolean {
  if (t1.fullname === "") { // Anonymous records
    return t2.fullname === ""
      && equalArraysWith(getRecordElements(t1),
        getRecordElements(t2),
        ([k1, v1], [k2, v2]) => k1 === k2 && equals(v1, v2));
  } else {
    return t1.fullname === t2.fullname
      && equalArraysWith(getGenerics(t1), getGenerics(t2), equals);
  }
}

export function class_type(
  fullname: string,
  generics?: TypeInfo[],
  construct?: Constructor,
  parent?: TypeInfo): TypeInfo {
  return new TypeInfo(fullname, generics, construct, parent);
}

export function record_type(
  fullname: string,
  generics: TypeInfo[],
  construct: Constructor,
  fields: () => FieldInfo[]): TypeInfo {
  return new TypeInfo(fullname, generics, construct, undefined, fields);
}

export function anonRecord_type(...fields: FieldInfo[]): TypeInfo {
  return new TypeInfo("", undefined, undefined, undefined, () => fields);
}

export function union_type(
  fullname: string,
  generics: TypeInfo[],
  construct: Constructor,
  cases: () => FieldInfo[][]): TypeInfo {
  const t: TypeInfo = new TypeInfo(fullname, generics, construct, undefined, undefined, () => {
    const caseNames = construct.prototype.cases() as string[];
    return cases().map((fields, i) => new CaseInfo(t, i, caseNames[i], fields))
  });
  return t;
}

export function tuple_type(...generics: TypeInfo[]): TypeInfo {
  return new TypeInfo("System.Tuple`" + generics.length, generics);
}

export function delegate_type(...generics: TypeInfo[]): TypeInfo {
  return new TypeInfo("System.Func`" + generics.length, generics);
}

export function lambda_type(argType: TypeInfo, returnType: TypeInfo): TypeInfo {
  return new TypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", [argType, returnType]);
}

export function option_type(generic: TypeInfo): TypeInfo {
  return new TypeInfo("Microsoft.FSharp.Core.FSharpOption`1", [generic]);
}

export function list_type(generic: TypeInfo): TypeInfo {
  return new TypeInfo("Microsoft.FSharp.Collections.FSharpList`1", [generic]);
}

export function array_type(generic: TypeInfo): TypeInfo {
  return new TypeInfo(generic.fullname + "[]", [generic]);
}

export function enum_type(fullname: string, underlyingType: TypeInfo, enumCases: EnumCase[]): TypeInfo {
  return new TypeInfo(fullname, [underlyingType], undefined, undefined, undefined, undefined, enumCases);
}

export const obj_type: TypeInfo = new TypeInfo("System.Object");
export const unit_type: TypeInfo = new TypeInfo("Microsoft.FSharp.Core.Unit");
export const char_type: TypeInfo = new TypeInfo("System.Char");
export const string_type: TypeInfo = new TypeInfo("System.String");
export const bool_type: TypeInfo = new TypeInfo("System.Boolean");
export const int8_type: TypeInfo = new TypeInfo("System.SByte");
export const uint8_type: TypeInfo = new TypeInfo("System.Byte");
export const int16_type: TypeInfo = new TypeInfo("System.Int16");
export const uint16_type: TypeInfo = new TypeInfo("System.UInt16");
export const int32_type: TypeInfo = new TypeInfo("System.Int32");
export const uint32_type: TypeInfo = new TypeInfo("System.UInt32");
export const float32_type: TypeInfo = new TypeInfo("System.Single");
export const float64_type: TypeInfo = new TypeInfo("System.Double");
export const decimal_type: TypeInfo = new TypeInfo("System.Decimal");

export function name(info: FieldInfo | TypeInfo | CaseInfo | MethodInfo): string {
  if (Array.isArray(info)) {
    return info[0];
  } else if (info instanceof TypeInfo) {
    const i = info.fullname.lastIndexOf(".");
    return i === -1 ? info.fullname : info.fullname.substr(i + 1);
  } else {
    return info.name;
  }
}

export function fullName(t: TypeInfo): string {
  const gen = t.generics != null && !isArray(t) ? t.generics : [];
  if (gen.length > 0) {
    return t.fullname + "[" + gen.map((x) => fullName(x)).join(",") + "]";
  } else {
    return t.fullname;
  }
}

export function namespace(t: TypeInfo) {
  const i = t.fullname.lastIndexOf(".");
  return i === -1 ? "" : t.fullname.substr(0, i);
}

export function isArray(t: TypeInfo): boolean {
  return t.fullname.endsWith("[]");
}

export function getElementType(t: TypeInfo): TypeInfo | undefined {
  return isArray(t) ? t.generics?.[0] : undefined;
}

export function isGenericType(t: TypeInfo) {
  return t.generics != null && t.generics.length > 0;
}

export function isEnum(t: TypeInfo) {
  return t.enumCases != null && t.enumCases.length > 0;
}

export function isSubclassOf(t1: TypeInfo, t2: TypeInfo) {
  return t1.parent?.Equals(t2) ?? false;
}

/**<commit_msg>Fix isSubclassOf to walk up the inheritance chain (#2536)<commit_after>import { FSharpRef, Record, Union } from "./Types.js";
import { combineHashCodes, equalArraysWith, IEquatable, stringHash } from "./Util.js";

export type FieldInfo = [string, TypeInfo];
export type PropertyInfo = FieldInfo;
export type ParameterInfo = FieldInfo;

export type Constructor = new (...args: any[]) => any;

export class CaseInfo {
  constructor(
    public declaringType: TypeInfo,
    public tag: number,
    public name: string,
    public fields?: FieldInfo[]) {
  }
}

export type EnumCase = [string, number];

export class MethodInfo {
  constructor(
    public name: string,
    public parameters: ParameterInfo[],
    public returnType: TypeInfo,
  ) {
  }
}

export class TypeInfo implements IEquatable<TypeInfo> {
  constructor(
    public fullname: string,
    public generics?: TypeInfo[],
    public construct?: Constructor,
    public parent?: TypeInfo,
    public fields?: () => FieldInfo[],
    public cases?: () => CaseInfo[],
    public enumCases?: EnumCase[]) {
  }
  public toString() {
    return fullName(this);
  }
  public GetHashCode() {
    return getHashCode(this);
  }
  public Equals(other: TypeInfo) {
    return equals(this, other);
  }
}

export function getGenerics(t: TypeInfo): TypeInfo[] {
  return t.generics != null ? t.generics : [];
}

export function getHashCode(t: TypeInfo) {
  const fullnameHash = stringHash(t.fullname);
  const genHashes: number[] = getGenerics(t).map(getHashCode);
  return combineHashCodes([fullnameHash, ...genHashes]);
}

export function equals(t1: TypeInfo, t2: TypeInfo): boolean {
  if (t1.fullname === "") { // Anonymous records
    return t2.fullname === ""
      && equalArraysWith(getRecordElements(t1),
        getRecordElements(t2),
        ([k1, v1], [k2, v2]) => k1 === k2 && equals(v1, v2));
  } else {
    return t1.fullname === t2.fullname
      && equalArraysWith(getGenerics(t1), getGenerics(t2), equals);
  }
}

export function class_type(
  fullname: string,
  generics?: TypeInfo[],
  construct?: Constructor,
  parent?: TypeInfo): TypeInfo {
  return new TypeInfo(fullname, generics, construct, parent);
}

export function record_type(
  fullname: string,
  generics: TypeInfo[],
  construct: Constructor,
  fields: () => FieldInfo[]): TypeInfo {
  return new TypeInfo(fullname, generics, construct, undefined, fields);
}

export function anonRecord_type(...fields: FieldInfo[]): TypeInfo {
  return new TypeInfo("", undefined, undefined, undefined, () => fields);
}

export function union_type(
  fullname: string,
  generics: TypeInfo[],
  construct: Constructor,
  cases: () => FieldInfo[][]): TypeInfo {
  const t: TypeInfo = new TypeInfo(fullname, generics, construct, undefined, undefined, () => {
    const caseNames = construct.prototype.cases() as string[];
    return cases().map((fields, i) => new CaseInfo(t, i, caseNames[i], fields))
  });
  return t;
}

export function tuple_type(...generics: TypeInfo[]): TypeInfo {
  return new TypeInfo("System.Tuple`" + generics.length, generics);
}

export function delegate_type(...generics: TypeInfo[]): TypeInfo {
  return new TypeInfo("System.Func`" + generics.length, generics);
}

export function lambda_type(argType: TypeInfo, returnType: TypeInfo): TypeInfo {
  return new TypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", [argType, returnType]);
}

export function option_type(generic: TypeInfo): TypeInfo {
  return new TypeInfo("Microsoft.FSharp.Core.FSharpOption`1", [generic]);
}

export function list_type(generic: TypeInfo): TypeInfo {
  return new TypeInfo("Microsoft.FSharp.Collections.FSharpList`1", [generic]);
}

export function array_type(generic: TypeInfo): TypeInfo {
  return new TypeInfo(generic.fullname + "[]", [generic]);
}

export function enum_type(fullname: string, underlyingType: TypeInfo, enumCases: EnumCase[]): TypeInfo {
  return new TypeInfo(fullname, [underlyingType], undefined, undefined, undefined, undefined, enumCases);
}

export const obj_type: TypeInfo = new TypeInfo("System.Object");
export const unit_type: TypeInfo = new TypeInfo("Microsoft.FSharp.Core.Unit");
export const char_type: TypeInfo = new TypeInfo("System.Char");
export const string_type: TypeInfo = new TypeInfo("System.String");
export const bool_type: TypeInfo = new TypeInfo("System.Boolean");
export const int8_type: TypeInfo = new TypeInfo("System.SByte");
export const uint8_type: TypeInfo = new TypeInfo("System.Byte");
export const int16_type: TypeInfo = new TypeInfo("System.Int16");
export const uint16_type: TypeInfo = new TypeInfo("System.UInt16");
export const int32_type: TypeInfo = new TypeInfo("System.Int32");
export const uint32_type: TypeInfo = new TypeInfo("System.UInt32");
export const float32_type: TypeInfo = new TypeInfo("System.Single");
export const float64_type: TypeInfo = new TypeInfo("System.Double");
export const decimal_type: TypeInfo = new TypeInfo("System.Decimal");

export function name(info: FieldInfo | TypeInfo | CaseInfo | MethodInfo): string {
  if (Array.isArray(info)) {
    return info[0];
  } else if (info instanceof TypeInfo) {
    const i = info.fullname.lastIndexOf(".");
    return i === -1 ? info.fullname : info.fullname.substr(i + 1);
  } else {
    return info.name;
  }
}

export function fullName(t: TypeInfo): string {
  const gen = t.generics != null && !isArray(t) ? t.generics : [];
  if (gen.length > 0) {
    return t.fullname + "[" + gen.map((x) => fullName(x)).join(",") + "]";
  } else {
    return t.fullname;
  }
}

export function namespace(t: TypeInfo) {
  const i = t.fullname.lastIndexOf(".");
  return i === -1 ? "" : t.fullname.substr(0, i);
}

export function isArray(t: TypeInfo): boolean {
  return t.fullname.endsWith("[]");
}

export function getElementType(t: TypeInfo): TypeInfo | undefined {
  return isArray(t) ? t.generics?.[0] : undefined;
}

export function isGenericType(t: TypeInfo) {
  return t.generics != null && t.generics.length > 0;
}

export function isEnum(t: TypeInfo) {
  return t.enumCases != null && t.enumCases.length > 0;
}

export function isSubclassOf(t1: TypeInfo, t2: TypeInfo): Boolean {
  return t1.parent != null && (t1.parent.Equals(t2) || isSubclassOf(t1.parent, t2));
}

/**
{'fraction_non_alphanumeric': 0.14208181449979748, 'fraction_numerical': 0.00963953017415958, 'mean_word_length': 4.70781322237633, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 31, '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': '4646247', 'n_tokens_mistral': 3901, 'n_tokens_neox': 3881, 'n_words': 1038}