packages feed

egison 5.0.0 → 5.1.0

raw patch · 178 files changed

+18730/−8342 lines, 178 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Language.Egison.AST: PDDivPat :: PDPatternBase var -> PDPatternBase var -> PDPatternBase var
- Language.Egison.AST: TEMathExpr :: TypeExpr
- Language.Egison.CmdOptions: [optMathExpr] :: EgisonOpts -> Maybe String
- Language.Egison.Data: IndexExprData :: Index ScalarData -> EgisonValue
- Language.Egison.Data: PolyExprData :: PolyExpr -> EgisonValue
- Language.Egison.Data: ScalarData :: ScalarData -> EgisonValue
- Language.Egison.Data: SymbolExprData :: SymbolExpr -> EgisonValue
- Language.Egison.Data: TermExprData :: TermExpr -> EgisonValue
- Language.Egison.Data: egisonToScalarData :: EgisonValue -> EvalM ScalarData
- Language.Egison.Data: extractScalar :: EgisonValue -> EvalM ScalarData
- Language.Egison.Data: getSymName :: EgisonValue -> String
- Language.Egison.Data: mathExprToEgison :: ScalarData -> EgisonValue
- Language.Egison.Data: symbolScalarData :: String -> String -> EgisonValue
- Language.Egison.Data: symbolScalarData' :: String -> ScalarData
- Language.Egison.IExpr: PDDivPat :: PDPatternBase var -> PDPatternBase var -> PDPatternBase var
- Language.Egison.Math: Apply1 :: ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math: Apply2 :: ScalarData -> ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math: Apply3 :: ScalarData -> ScalarData -> ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math: Apply4 :: ScalarData -> ScalarData -> ScalarData -> ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math: Div :: PolyExpr -> PolyExpr -> ScalarData
- Language.Egison.Math: FunctionData :: ScalarData -> [ScalarData] -> SymbolExpr
- Language.Egison.Math: Plus :: [TermExpr] -> PolyExpr
- Language.Egison.Math: Quote :: ScalarData -> SymbolExpr
- Language.Egison.Math: QuoteFunction :: WHNFData -> SymbolExpr
- Language.Egison.Math: Symbol :: Id -> String -> [Index ScalarData] -> SymbolExpr
- Language.Egison.Math: Term :: Integer -> Monomial -> TermExpr
- Language.Egison.Math: class Printable a
- Language.Egison.Math: data ScalarData
- Language.Egison.Math: data SymbolExpr
- Language.Egison.Math: data TermExpr
- Language.Egison.Math: isAtom :: Printable a => a -> Bool
- Language.Egison.Math: makeApplyExpr :: ScalarData -> [ScalarData] -> SymbolExpr
- Language.Egison.Math: mathDenominator :: ScalarData -> ScalarData
- Language.Egison.Math: mathDiv :: ScalarData -> ScalarData -> ScalarData
- Language.Egison.Math: mathMult :: ScalarData -> ScalarData -> ScalarData
- Language.Egison.Math: mathNegate :: ScalarData -> ScalarData
- Language.Egison.Math: mathNormalize' :: ScalarData -> ScalarData
- Language.Egison.Math: mathNumerator :: ScalarData -> ScalarData
- Language.Egison.Math: mathPlus :: ScalarData -> ScalarData -> ScalarData
- Language.Egison.Math: newtype PolyExpr
- Language.Egison.Math: pattern SingleSymbol :: SymbolExpr -> ScalarData
- Language.Egison.Math: pattern SingleTerm :: Integer -> Monomial -> ScalarData
- Language.Egison.Math: pattern ZeroExpr :: ScalarData
- Language.Egison.Math: pretty :: Printable a => a -> String
- Language.Egison.Math: rewriteSymbol :: ScalarData -> ScalarData
- Language.Egison.Math: type Monomial = [(SymbolExpr, Integer)]
- Language.Egison.Math.Arith: mathDenominator :: ScalarData -> ScalarData
- Language.Egison.Math.Arith: mathDiv :: ScalarData -> ScalarData -> ScalarData
- Language.Egison.Math.Arith: mathMinus :: ScalarData -> ScalarData -> ScalarData
- Language.Egison.Math.Arith: mathMult :: ScalarData -> ScalarData -> ScalarData
- Language.Egison.Math.Arith: mathNumerator :: ScalarData -> ScalarData
- Language.Egison.Math.Arith: mathPlus :: ScalarData -> ScalarData -> ScalarData
- Language.Egison.Math.Arith: mathPower :: ScalarData -> Integer -> ScalarData
- Language.Egison.Math.Expr: Apply1 :: ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math.Expr: Apply2 :: ScalarData -> ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math.Expr: Apply3 :: ScalarData -> ScalarData -> ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math.Expr: Apply4 :: ScalarData -> ScalarData -> ScalarData -> ScalarData -> ScalarData -> SymbolExpr
- Language.Egison.Math.Expr: Div :: PolyExpr -> PolyExpr -> ScalarData
- Language.Egison.Math.Expr: FunctionData :: ScalarData -> [ScalarData] -> SymbolExpr
- Language.Egison.Math.Expr: Plus :: [TermExpr] -> PolyExpr
- Language.Egison.Math.Expr: Quote :: ScalarData -> SymbolExpr
- Language.Egison.Math.Expr: QuoteFunction :: WHNFData -> SymbolExpr
- Language.Egison.Math.Expr: ScalarM :: ScalarM
- Language.Egison.Math.Expr: Symbol :: Id -> String -> [Index ScalarData] -> SymbolExpr
- Language.Egison.Math.Expr: SymbolM :: SymbolM
- Language.Egison.Math.Expr: Term :: Integer -> Monomial -> TermExpr
- Language.Egison.Math.Expr: TermM :: TermM
- Language.Egison.Math.Expr: apply1 :: Pattern (PP String, PP WHNFData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData)
- Language.Egison.Math.Expr: apply1M :: SymbolM -> p -> (Eql, Something, ScalarM)
- Language.Egison.Math.Expr: apply2 :: Pattern (PP String, PP WHNFData, PP ScalarData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData, ScalarData)
- Language.Egison.Math.Expr: apply2M :: SymbolM -> p -> (Eql, Something, ScalarM, ScalarM)
- Language.Egison.Math.Expr: apply3 :: Pattern (PP String, PP WHNFData, PP ScalarData, PP ScalarData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData, ScalarData, ScalarData)
- Language.Egison.Math.Expr: apply3M :: SymbolM -> p -> (Eql, Something, ScalarM, ScalarM, ScalarM)
- Language.Egison.Math.Expr: apply4 :: Pattern (PP String, PP WHNFData, PP ScalarData, PP ScalarData, PP ScalarData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData, ScalarData, ScalarData, ScalarData)
- Language.Egison.Math.Expr: apply4M :: SymbolM -> p -> (Eql, Something, ScalarM, ScalarM, ScalarM, ScalarM)
- Language.Egison.Math.Expr: class Printable a
- Language.Egison.Math.Expr: data ScalarData
- Language.Egison.Math.Expr: data ScalarM
- Language.Egison.Math.Expr: data SymbolExpr
- Language.Egison.Math.Expr: data SymbolM
- Language.Egison.Math.Expr: data TermExpr
- Language.Egison.Math.Expr: data TermM
- Language.Egison.Math.Expr: equalMonomial :: Pattern (PP Integer, PP Monomial) (Multiset (SymbolM, Eql)) Monomial (Integer, Monomial)
- Language.Egison.Math.Expr: equalMonomialM :: Multiset (SymbolM, Eql) -> p -> (Eql, Multiset (SymbolM, Eql))
- Language.Egison.Math.Expr: func :: Pattern (PP ScalarData, PP [ScalarData]) SymbolM SymbolExpr (ScalarData, [ScalarData])
- Language.Egison.Math.Expr: funcM :: SymbolM -> SymbolExpr -> (ScalarM, List ScalarM)
- Language.Egison.Math.Expr: instance Control.Egison.Matcher.Matcher Language.Egison.Math.Expr.ScalarM Language.Egison.Math.Expr.ScalarData
- Language.Egison.Math.Expr: instance Control.Egison.Matcher.Matcher Language.Egison.Math.Expr.SymbolM Language.Egison.Math.Expr.SymbolExpr
- Language.Egison.Math.Expr: instance Control.Egison.Matcher.Matcher Language.Egison.Math.Expr.TermM Language.Egison.Math.Expr.TermExpr
- Language.Egison.Math.Expr: instance Control.Egison.Matcher.ValuePattern Language.Egison.Math.Expr.ScalarM Language.Egison.Math.Expr.ScalarData
- Language.Egison.Math.Expr: instance Control.Egison.Matcher.ValuePattern Language.Egison.Math.Expr.SymbolM Language.Egison.Math.Expr.SymbolExpr
- Language.Egison.Math.Expr: instance GHC.Classes.Eq Language.Egison.Math.Expr.PolyExpr
- Language.Egison.Math.Expr: instance GHC.Classes.Eq Language.Egison.Math.Expr.ScalarData
- Language.Egison.Math.Expr: instance GHC.Classes.Eq Language.Egison.Math.Expr.SymbolExpr
- Language.Egison.Math.Expr: instance GHC.Classes.Eq Language.Egison.Math.Expr.TermExpr
- Language.Egison.Math.Expr: instance GHC.Show.Show (Language.Egison.IExpr.Index Language.Egison.Math.Expr.ScalarData)
- Language.Egison.Math.Expr: instance GHC.Show.Show Language.Egison.Math.Expr.PolyExpr
- Language.Egison.Math.Expr: instance GHC.Show.Show Language.Egison.Math.Expr.ScalarData
- Language.Egison.Math.Expr: instance GHC.Show.Show Language.Egison.Math.Expr.SymbolExpr
- Language.Egison.Math.Expr: instance GHC.Show.Show Language.Egison.Math.Expr.TermExpr
- Language.Egison.Math.Expr: instance Language.Egison.Math.Expr.Printable Language.Egison.Math.Expr.PolyExpr
- Language.Egison.Math.Expr: instance Language.Egison.Math.Expr.Printable Language.Egison.Math.Expr.ScalarData
- Language.Egison.Math.Expr: instance Language.Egison.Math.Expr.Printable Language.Egison.Math.Expr.SymbolExpr
- Language.Egison.Math.Expr: instance Language.Egison.Math.Expr.Printable Language.Egison.Math.Expr.TermExpr
- Language.Egison.Math.Expr: isAtom :: Printable a => a -> Bool
- Language.Egison.Math.Expr: makeApplyExpr :: ScalarData -> [ScalarData] -> SymbolExpr
- Language.Egison.Math.Expr: mathNegate :: ScalarData -> ScalarData
- Language.Egison.Math.Expr: mathScalarMult :: Integer -> ScalarData -> ScalarData
- Language.Egison.Math.Expr: negQuote :: Pattern (PP ScalarData) SymbolM SymbolExpr ScalarData
- Language.Egison.Math.Expr: negQuoteM :: SymbolM -> p -> ScalarM
- Language.Egison.Math.Expr: newtype PolyExpr
- Language.Egison.Math.Expr: pattern SingleSymbol :: SymbolExpr -> ScalarData
- Language.Egison.Math.Expr: pattern SingleTerm :: Integer -> Monomial -> ScalarData
- Language.Egison.Math.Expr: pattern ZeroExpr :: ScalarData
- Language.Egison.Math.Expr: pretty :: Printable a => a -> String
- Language.Egison.Math.Expr: quote :: Pattern (PP ScalarData) SymbolM SymbolExpr ScalarData
- Language.Egison.Math.Expr: quoteFunction :: Pattern (PP String, PP WHNFData) SymbolM SymbolExpr (String, WHNFData)
- Language.Egison.Math.Expr: quoteFunctionM :: SymbolM -> p -> Eql
- Language.Egison.Math.Expr: singleTerm :: Pattern (PP Integer, PP Integer, PP Monomial) ScalarM ScalarData (Integer, Integer, Monomial)
- Language.Egison.Math.Expr: singleTermM :: ScalarM -> p -> (Eql, Eql, Multiset (SymbolM, Eql))
- Language.Egison.Math.Expr: symbol :: Pattern (PP String) SymbolM SymbolExpr String
- Language.Egison.Math.Expr: symbolM :: SymbolM -> p -> Eql
- Language.Egison.Math.Expr: term :: Pattern (PP Integer, PP Monomial) TermM TermExpr (Integer, Monomial)
- Language.Egison.Math.Expr: termM :: TermM -> TermExpr -> (Eql, Multiset (SymbolM, Eql))
- Language.Egison.Math.Expr: type Monomial = [(SymbolExpr, Integer)]
- Language.Egison.Math.Expr: zero :: Pattern () ScalarM ScalarData ()
- Language.Egison.Math.Expr: zeroM :: ScalarM -> p -> ()
- Language.Egison.Math.Normalize: mathDivideTerm :: TermExpr -> TermExpr -> TermExpr
- Language.Egison.Math.Normalize: mathNormalize' :: ScalarData -> ScalarData
- Language.Egison.Math.Normalize: termsGcd :: [TermExpr] -> TermExpr
- Language.Egison.Math.Rewrite: rewriteSymbol :: ScalarData -> ScalarData
- Language.Egison.PrettyMath.AST: class ToMathExpr a
- Language.Egison.PrettyMath.AST: data MathExpr
- Language.Egison.PrettyMath.AST: instance GHC.Classes.Eq Language.Egison.PrettyMath.AST.MathExpr
- Language.Egison.PrettyMath.AST: instance GHC.Show.Show Language.Egison.PrettyMath.AST.MathExpr
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr (Language.Egison.Math.Expr.SymbolExpr, GHC.Num.Integer.Integer)
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr GHC.Num.Integer.Integer
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr Language.Egison.Data.EgisonValue
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr Language.Egison.Math.Expr.PolyExpr
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr Language.Egison.Math.Expr.ScalarData
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr Language.Egison.Math.Expr.SymbolExpr
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr Language.Egison.Math.Expr.TermExpr
- Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathExpr a => Language.Egison.PrettyMath.AST.ToMathExpr (Language.Egison.Data.Tensor a)
- Language.Egison.PrettyMath.AST: toMathExpr :: ToMathExpr a => a -> MathExpr
- Language.Egison.PrettyMath.AsciiMath: showMathExpr :: MathExpr -> String
- Language.Egison.PrettyMath.Latex: showMathExpr :: MathExpr -> String
- Language.Egison.PrettyMath.Mathematica: showMathExpr :: MathExpr -> String
- Language.Egison.PrettyMath.Maxima: showMathExpr :: MathExpr -> String
- Language.Egison.Type.Env: [classParam] :: ClassInfo -> TyVar
- Language.Egison.Type.Env: [instType] :: InstanceInfo -> Type
- Language.Egison.Type.Types: TMathExpr :: Type
- Language.Egison.Type.Types: [classParam] :: ClassInfo -> TyVar
- Language.Egison.Type.Types: [constraintType] :: Constraint -> Type
- Language.Egison.Type.Types: [instType] :: InstanceInfo -> Type
+ Language.Egison.AST: DeclareApply :: String -> [String] -> Expr -> TopExpr
+ Language.Egison.AST: DeclareCasQuotient :: String -> TypeExpr -> Expr -> TopExpr
+ Language.Egison.AST: DeclareCasSubtype :: TypeExpr -> TypeExpr -> TopExpr
+ Language.Egison.AST: DeclareCasType :: String -> TypeExpr -> TopExpr
+ Language.Egison.AST: DeclareDerivative :: String -> Expr -> TopExpr
+ Language.Egison.AST: DeclareIdeal :: [Expr] -> TopExpr
+ Language.Egison.AST: DeclareMathFunc :: String -> Maybe TypeExpr -> TopExpr
+ Language.Egison.AST: DeclareRule :: Maybe String -> RuleLevel -> Pattern -> Expr -> TopExpr
+ Language.Egison.AST: FracRuleLevel :: RuleLevel
+ Language.Egison.AST: PDFracPat :: PDPatternBase var -> PDPatternBase var -> PDPatternBase var
+ Language.Egison.AST: PolyRuleLevel :: RuleLevel
+ Language.Egison.AST: SSEClosed :: [TypeAtomExpr] -> SymbolSetExpr
+ Language.Egison.AST: SSEOpen :: SymbolSetExpr
+ Language.Egison.AST: SimplifyUsingExpr :: Expr -> String -> Expr
+ Language.Egison.AST: TAEApp :: String -> [TypeAtomExpr] -> TypeAtomExpr
+ Language.Egison.AST: TAEInt :: Integer -> TypeAtomExpr
+ Language.Egison.AST: TAEName :: String -> TypeAtomExpr
+ Language.Egison.AST: TEFactor :: TypeExpr
+ Language.Egison.AST: TEFrac :: TypeExpr -> TypeExpr
+ Language.Egison.AST: TEMatcherSlot :: TypeExpr -> TypeExpr -> TypeExpr
+ Language.Egison.AST: TEMathValue :: TypeExpr
+ Language.Egison.AST: TEPoly :: TypeExpr -> SymbolSetExpr -> TypeExpr
+ Language.Egison.AST: TETerm :: TypeExpr -> SymbolSetExpr -> TypeExpr
+ Language.Egison.AST: TermRuleLevel :: RuleLevel
+ Language.Egison.AST: data RuleLevel
+ Language.Egison.AST: data SymbolSetExpr
+ Language.Egison.AST: data TypeAtomExpr
+ Language.Egison.AST: instance GHC.Classes.Eq Language.Egison.AST.RuleLevel
+ Language.Egison.AST: instance GHC.Classes.Eq Language.Egison.AST.SymbolSetExpr
+ Language.Egison.AST: instance GHC.Classes.Eq Language.Egison.AST.TypeAtomExpr
+ Language.Egison.AST: instance GHC.Classes.Ord Language.Egison.AST.TypeAtomExpr
+ Language.Egison.AST: instance GHC.Show.Show Language.Egison.AST.RuleLevel
+ Language.Egison.AST: instance GHC.Show.Show Language.Egison.AST.SymbolSetExpr
+ Language.Egison.AST: instance GHC.Show.Show Language.Egison.AST.TypeAtomExpr
+ Language.Egison.CmdOptions: [optMatcherConsistencyWarnings] :: EgisonOpts -> Bool
+ Language.Egison.CmdOptions: [optMathValue] :: EgisonOpts -> Maybe String
+ Language.Egison.Core: applyObj :: Env -> WHNFData -> [Object] -> EvalM WHNFData
+ Language.Egison.Core: applyRef :: Env -> WHNFData -> [ObjectRef] -> EvalM WHNFData
+ Language.Egison.Data: CASData :: CASValue -> EgisonValue
+ Language.Egison.Data: CASFactor :: SymbolExpr -> CASValue
+ Language.Egison.Data: CASFrac :: CASValue -> CASValue -> CASValue
+ Language.Egison.Data: CASIndexData :: Index CASValue -> EgisonValue
+ Language.Egison.Data: CASInteger :: Integer -> CASValue
+ Language.Egison.Data: CASPoly :: [CASTerm] -> CASValue
+ Language.Egison.Data: CASTerm :: CASValue -> Monomial -> CASTerm
+ Language.Egison.Data: applyCASData :: CASValue -> [CASValue] -> EgisonValue
+ Language.Egison.Data: data CASTerm
+ Language.Egison.Data: data CASValue
+ Language.Egison.Data: extractCASValue :: EgisonValue -> EvalM CASValue
+ Language.Egison.Data: functionCASData :: CASValue -> [CASValue] -> EgisonValue
+ Language.Egison.Data: quoteCASData :: CASValue -> EgisonValue
+ Language.Egison.Data: quoteFunctionCASData :: WHNFData -> EgisonValue
+ Language.Egison.Data: symbolCASData :: String -> String -> EgisonValue
+ Language.Egison.EnvBuilder: [ebrCasSubtypeEdges] :: EnvBuildResult -> [(Type, Type)]
+ Language.Egison.EnvBuilder: [ebrCasTypeAliases] :: EnvBuildResult -> HashMap String Type
+ Language.Egison.EnvBuilder: [ebrDerivativeRules] :: EnvBuildResult -> [(String, Expr)]
+ Language.Egison.EnvBuilder: [ebrMathFuncNames] :: EnvBuildResult -> Set String
+ Language.Egison.EnvBuilder: [ebrReductionRules] :: EnvBuildResult -> [(Maybe String, RuleLevel, Pattern, Expr)]
+ Language.Egison.EvalState: [autoRuleTriggers] :: EvalState -> [Set String]
+ Language.Egison.EvalState: [autoRuleVarNames] :: EvalState -> [String]
+ Language.Egison.EvalState: [casSubtypeEdges] :: EvalState -> [(Type, Type)]
+ Language.Egison.EvalState: [casTypeAliasEnv] :: EvalState -> HashMap String Type
+ Language.Egison.EvalState: [declaredSymbolOrder] :: EvalState -> [String]
+ Language.Egison.EvalState: [derivativeRuleNames] :: EvalState -> [String]
+ Language.Egison.EvalState: [derivativeRulesCount] :: EvalState -> Int
+ Language.Egison.EvalState: [derivativesDesugared] :: EvalState -> [String]
+ Language.Egison.EvalState: [patternFuncStructEnv] :: EvalState -> PatternTypeEnv
+ Language.Egison.EvalState: [reductionRuleNames] :: EvalState -> [String]
+ Language.Egison.EvalState: [reductionRulesCount] :: EvalState -> Int
+ Language.Egison.EvalState: appendAutoRuleTriggers :: MonadEval m => [String] -> m ()
+ Language.Egison.EvalState: appendAutoRuleVarName :: MonadEval m => String -> m ()
+ Language.Egison.EvalState: appendDeclaredSymbols :: MonadEval m => [String] -> m ()
+ Language.Egison.EvalState: appendDerivativeDesugared :: MonadEval m => String -> m ()
+ Language.Egison.EvalState: getAutoRuleTriggers :: MonadEval m => m [Set String]
+ Language.Egison.EvalState: getAutoRuleVarNames :: MonadEval m => m [String]
+ Language.Egison.EvalState: getCasSubtypeEdges :: MonadEval m => m [(Type, Type)]
+ Language.Egison.EvalState: getCasTypeAliasEnv :: MonadEval m => m (HashMap String Type)
+ Language.Egison.EvalState: getDeclaredSymbolOrder :: MonadEval m => m [String]
+ Language.Egison.EvalState: getDerivativeRuleNames :: MonadEval m => m [String]
+ Language.Egison.EvalState: getDerivativeRulesCount :: MonadEval m => m Int
+ Language.Egison.EvalState: getDerivativesDesugared :: MonadEval m => m [String]
+ Language.Egison.EvalState: getPatternFuncStructEnv :: MonadEval m => m PatternTypeEnv
+ Language.Egison.EvalState: getReductionRuleNames :: MonadEval m => m [String]
+ Language.Egison.EvalState: getReductionRulesCount :: MonadEval m => m Int
+ Language.Egison.EvalState: setAutoRuleVarNames :: MonadEval m => [String] -> m ()
+ Language.Egison.EvalState: setCasSubtypeEdges :: MonadEval m => [(Type, Type)] -> m ()
+ Language.Egison.EvalState: setCasTypeAliasEnv :: MonadEval m => HashMap String Type -> m ()
+ Language.Egison.EvalState: setDerivativeRuleNames :: MonadEval m => [String] -> m ()
+ Language.Egison.EvalState: setDerivativeRulesCount :: MonadEval m => Int -> m ()
+ Language.Egison.EvalState: setDerivativesDesugared :: MonadEval m => [String] -> m ()
+ Language.Egison.EvalState: setPatternFuncStructEnv :: MonadEval m => PatternTypeEnv -> m ()
+ Language.Egison.EvalState: setReductionRuleNames :: MonadEval m => [String] -> m ()
+ Language.Egison.EvalState: setReductionRulesCount :: MonadEval m => Int -> m ()
+ Language.Egison.IExpr: IReshape :: Type -> IExpr -> IExpr
+ Language.Egison.IExpr: IRuntimeDispatch :: String -> String -> [(Type, String)] -> [IExpr] -> IExpr
+ Language.Egison.IExpr: PDFracPat :: PDPatternBase var -> PDPatternBase var -> PDPatternBase var
+ Language.Egison.IExpr: TIReshape :: Type -> TIExpr -> TIExprNode
+ Language.Egison.IExpr: TIRuntimeDispatch :: String -> String -> [(Type, String)] -> [TIExpr] -> TIExprNode
+ Language.Egison.IExpr: mapTIExprChildren :: (TIExpr -> TIExpr) -> TIExprNode -> TIExprNode
+ Language.Egison.Math: CASFactor :: SymbolExpr -> CASValue
+ Language.Egison.Math: CASFrac :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math: CASInteger :: Integer -> CASValue
+ Language.Egison.Math: CASM :: CASM
+ Language.Egison.Math: CASPoly :: [CASTerm] -> CASValue
+ Language.Egison.Math: CASSymbolM :: CASSymbolM
+ Language.Egison.Math: CASTerm :: CASValue -> Monomial -> CASTerm
+ Language.Egison.Math: CASTermM :: CASTermM
+ Language.Egison.Math: casApply1 :: Pattern (PP String, PP WHNFData, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue)
+ Language.Egison.Math: casApply1M :: CASSymbolM -> p -> (Eql, Something, CASM)
+ Language.Egison.Math: casApply2 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue)
+ Language.Egison.Math: casApply2M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM)
+ Language.Egison.Math: casApply3 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue, CASValue)
+ Language.Egison.Math: casApply3M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM, CASM)
+ Language.Egison.Math: casApply4 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue, CASValue, CASValue)
+ Language.Egison.Math: casApply4M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM, CASM, CASM)
+ Language.Egison.Math: casDenominator :: CASValue -> CASValue
+ Language.Egison.Math: casDivide :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math: casEqualMonomial :: Pattern (PP Integer, PP Monomial) (Multiset (CASSymbolM, Eql)) Monomial (Integer, Monomial)
+ Language.Egison.Math: casEqualMonomialM :: Multiset (CASSymbolM, Eql) -> p -> (Eql, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math: casFunc :: Pattern (PP CASValue, PP [CASValue]) CASSymbolM SymbolExpr (CASValue, [CASValue])
+ Language.Egison.Math: casFuncM :: CASSymbolM -> SymbolExpr -> (CASM, List CASM)
+ Language.Egison.Math: casIsAtom :: CASValue -> Bool
+ Language.Egison.Math: casIsZero :: CASValue -> Bool
+ Language.Egison.Math: casMinus :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math: casMult :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math: casNegQuote :: Pattern (PP CASValue) CASSymbolM SymbolExpr CASValue
+ Language.Egison.Math: casNegQuoteM :: CASSymbolM -> p -> CASM
+ Language.Egison.Math: casNegate :: CASValue -> CASValue
+ Language.Egison.Math: casNormalize :: CASValue -> CASValue
+ Language.Egison.Math: casNumerator :: CASValue -> CASValue
+ Language.Egison.Math: casPlus :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math: casPower :: CASValue -> Integer -> CASValue
+ Language.Egison.Math: casQuote :: Pattern (PP CASValue) CASSymbolM SymbolExpr CASValue
+ Language.Egison.Math: casQuoteFunction :: Pattern (PP String, PP WHNFData) CASSymbolM SymbolExpr (String, WHNFData)
+ Language.Egison.Math: casQuoteFunctionM :: CASSymbolM -> p -> Eql
+ Language.Egison.Math: casRewriteSymbol :: CASValue -> CASValue
+ Language.Egison.Math: casSingleTerm :: Pattern (PP Integer, PP Integer, PP Monomial) CASM CASValue (Integer, Integer, Monomial)
+ Language.Egison.Math: casSingleTermM :: CASM -> p -> (Eql, Eql, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math: casSymbol :: Pattern (PP String) CASSymbolM SymbolExpr String
+ Language.Egison.Math: casSymbolM :: CASSymbolM -> p -> Eql
+ Language.Egison.Math: casTerm' :: Pattern (PP CASValue, PP Monomial) CASTermM CASTerm (CASValue, Monomial)
+ Language.Egison.Math: casTerm'M :: CASTermM -> CASTerm -> (CASM, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math: casTermM :: CASTermM -> CASTerm -> (CASM, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math: casZero :: Pattern () CASM CASValue ()
+ Language.Egison.Math: casZeroM :: CASM -> p -> ()
+ Language.Egison.Math: data CASM
+ Language.Egison.Math: data CASSymbolM
+ Language.Egison.Math: data CASTerm
+ Language.Egison.Math: data CASTermM
+ Language.Egison.Math: data CASValue
+ Language.Egison.Math: pattern CASSingleSymbol :: SymbolExpr -> CASValue
+ Language.Egison.Math: pattern CASSingleTerm :: Integer -> Monomial -> CASValue
+ Language.Egison.Math: pattern CASZero :: CASValue
+ Language.Egison.Math: prettyCAS :: CASValue -> String
+ Language.Egison.Math.CAS: Apply1 :: CASValue -> CASValue -> SymbolExpr
+ Language.Egison.Math.CAS: Apply2 :: CASValue -> CASValue -> CASValue -> SymbolExpr
+ Language.Egison.Math.CAS: Apply3 :: CASValue -> CASValue -> CASValue -> CASValue -> SymbolExpr
+ Language.Egison.Math.CAS: Apply4 :: CASValue -> CASValue -> CASValue -> CASValue -> CASValue -> SymbolExpr
+ Language.Egison.Math.CAS: CASFactor :: SymbolExpr -> CASValue
+ Language.Egison.Math.CAS: CASFrac :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math.CAS: CASInteger :: Integer -> CASValue
+ Language.Egison.Math.CAS: CASM :: CASM
+ Language.Egison.Math.CAS: CASPoly :: [CASTerm] -> CASValue
+ Language.Egison.Math.CAS: CASSymbolM :: CASSymbolM
+ Language.Egison.Math.CAS: CASTerm :: CASValue -> Monomial -> CASTerm
+ Language.Egison.Math.CAS: CASTermM :: CASTermM
+ Language.Egison.Math.CAS: FunctionData :: CASValue -> [CASValue] -> SymbolExpr
+ Language.Egison.Math.CAS: Quote :: CASValue -> SymbolExpr
+ Language.Egison.Math.CAS: QuoteFunction :: WHNFData -> SymbolExpr
+ Language.Egison.Math.CAS: Symbol :: Id -> String -> [Index CASValue] -> SymbolExpr
+ Language.Egison.Math.CAS: casApply1 :: Pattern (PP String, PP WHNFData, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue)
+ Language.Egison.Math.CAS: casApply1M :: CASSymbolM -> p -> (Eql, Something, CASM)
+ Language.Egison.Math.CAS: casApply2 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue)
+ Language.Egison.Math.CAS: casApply2M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM)
+ Language.Egison.Math.CAS: casApply3 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue, CASValue)
+ Language.Egison.Math.CAS: casApply3M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM, CASM)
+ Language.Egison.Math.CAS: casApply4 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue, CASValue, CASValue)
+ Language.Egison.Math.CAS: casApply4M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM, CASM, CASM)
+ Language.Egison.Math.CAS: casAtomSet :: CASValue -> [String]
+ Language.Egison.Math.CAS: casDenominator :: CASValue -> CASValue
+ Language.Egison.Math.CAS: casDifferentialClosed :: CASValue -> CASValue -> Bool
+ Language.Egison.Math.CAS: casDivide :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math.CAS: casEqualMonomial :: Pattern (PP Integer, PP Monomial) (Multiset (CASSymbolM, Eql)) Monomial (Integer, Monomial)
+ Language.Egison.Math.CAS: casEqualMonomialM :: Multiset (CASSymbolM, Eql) -> p -> (Eql, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math.CAS: casFactor :: SymbolExpr -> CASValue
+ Language.Egison.Math.CAS: casFrac :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math.CAS: casFunc :: Pattern (PP CASValue, PP [CASValue]) CASSymbolM SymbolExpr (CASValue, [CASValue])
+ Language.Egison.Math.CAS: casFuncM :: CASSymbolM -> SymbolExpr -> (CASM, List CASM)
+ Language.Egison.Math.CAS: casGcd :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math.CAS: casInteger :: Integer -> CASValue
+ Language.Egison.Math.CAS: casIsAtom :: CASValue -> Bool
+ Language.Egison.Math.CAS: casIsZero :: CASValue -> Bool
+ Language.Egison.Math.CAS: casMinus :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math.CAS: casMult :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math.CAS: casNegQuote :: Pattern (PP CASValue) CASSymbolM SymbolExpr CASValue
+ Language.Egison.Math.CAS: casNegQuoteM :: CASSymbolM -> p -> CASM
+ Language.Egison.Math.CAS: casNegate :: CASValue -> CASValue
+ Language.Egison.Math.CAS: casNormalize :: CASValue -> CASValue
+ Language.Egison.Math.CAS: casNormalizePoly :: [CASTerm] -> CASValue
+ Language.Egison.Math.CAS: casNumerator :: CASValue -> CASValue
+ Language.Egison.Math.CAS: casPlus :: CASValue -> CASValue -> CASValue
+ Language.Egison.Math.CAS: casPoly :: [CASTerm] -> CASValue
+ Language.Egison.Math.CAS: casPower :: CASValue -> Integer -> CASValue
+ Language.Egison.Math.CAS: casQuote :: Pattern (PP CASValue) CASSymbolM SymbolExpr CASValue
+ Language.Egison.Math.CAS: casQuoteFunction :: Pattern (PP String, PP WHNFData) CASSymbolM SymbolExpr (String, WHNFData)
+ Language.Egison.Math.CAS: casQuoteFunctionM :: CASSymbolM -> p -> Eql
+ Language.Egison.Math.CAS: casReshapeAs :: Type -> CASValue -> CASValue
+ Language.Egison.Math.CAS: casSingleTerm :: Pattern (PP Integer, PP Integer, PP Monomial) CASM CASValue (Integer, Integer, Monomial)
+ Language.Egison.Math.CAS: casSingleTermM :: CASM -> p -> (Eql, Eql, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math.CAS: casSymbol :: Pattern (PP String) CASSymbolM SymbolExpr String
+ Language.Egison.Math.CAS: casSymbolM :: CASSymbolM -> p -> Eql
+ Language.Egison.Math.CAS: casTerm :: CASValue -> Monomial -> CASTerm
+ Language.Egison.Math.CAS: casTerm' :: Pattern (PP CASValue, PP Monomial) CASTermM CASTerm (CASValue, Monomial)
+ Language.Egison.Math.CAS: casTerm'M :: CASTermM -> CASTerm -> (CASM, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math.CAS: casTermM :: CASTermM -> CASTerm -> (CASM, Multiset (CASSymbolM, Eql))
+ Language.Egison.Math.CAS: casTermsGcd :: [CASTerm] -> CASTerm
+ Language.Egison.Math.CAS: casZero :: Pattern () CASM CASValue ()
+ Language.Egison.Math.CAS: casZeroM :: CASM -> p -> ()
+ Language.Egison.Math.CAS: data CASM
+ Language.Egison.Math.CAS: data CASSymbolM
+ Language.Egison.Math.CAS: data CASTerm
+ Language.Egison.Math.CAS: data CASTermM
+ Language.Egison.Math.CAS: data CASValue
+ Language.Egison.Math.CAS: data SymbolExpr
+ Language.Egison.Math.CAS: instance Control.Egison.Matcher.Matcher Language.Egison.Math.CAS.CASM Language.Egison.Math.CAS.CASValue
+ Language.Egison.Math.CAS: instance Control.Egison.Matcher.Matcher Language.Egison.Math.CAS.CASSymbolM Language.Egison.Math.CAS.SymbolExpr
+ Language.Egison.Math.CAS: instance Control.Egison.Matcher.Matcher Language.Egison.Math.CAS.CASTermM Language.Egison.Math.CAS.CASTerm
+ Language.Egison.Math.CAS: instance Control.Egison.Matcher.ValuePattern Language.Egison.Math.CAS.CASM Language.Egison.Math.CAS.CASValue
+ Language.Egison.Math.CAS: instance Control.Egison.Matcher.ValuePattern Language.Egison.Math.CAS.CASSymbolM Language.Egison.Math.CAS.SymbolExpr
+ Language.Egison.Math.CAS: instance GHC.Classes.Eq Language.Egison.Math.CAS.CASTerm
+ Language.Egison.Math.CAS: instance GHC.Classes.Eq Language.Egison.Math.CAS.CASValue
+ Language.Egison.Math.CAS: instance GHC.Classes.Eq Language.Egison.Math.CAS.NestedCoeffPolicy
+ Language.Egison.Math.CAS: instance GHC.Classes.Eq Language.Egison.Math.CAS.SymbolExpr
+ Language.Egison.Math.CAS: instance GHC.Show.Show Language.Egison.Math.CAS.CASTerm
+ Language.Egison.Math.CAS: instance GHC.Show.Show Language.Egison.Math.CAS.CASValue
+ Language.Egison.Math.CAS: instance GHC.Show.Show Language.Egison.Math.CAS.SymbolExpr
+ Language.Egison.Math.CAS: makeApplyExpr :: CASValue -> [CASValue] -> SymbolExpr
+ Language.Egison.Math.CAS: pattern CASSingleSymbol :: SymbolExpr -> CASValue
+ Language.Egison.Math.CAS: pattern CASSingleTerm :: Integer -> Monomial -> CASValue
+ Language.Egison.Math.CAS: pattern CASZero :: CASValue
+ Language.Egison.Math.CAS: prettyCAS :: CASValue -> String
+ Language.Egison.Math.CAS: prettyFunctionName :: WHNFData -> Maybe String
+ Language.Egison.Math.CAS: prettyTypeOf :: CASValue -> String
+ Language.Egison.Math.CAS: type Id = String
+ Language.Egison.Math.CAS: type Monomial = [(SymbolExpr, Integer)]
+ Language.Egison.Math.Rewrite: casRewriteSymbol :: CASValue -> CASValue
+ Language.Egison.PrettyMath.AST: class ToMathValue a
+ Language.Egison.PrettyMath.AST: data MathValue
+ Language.Egison.PrettyMath.AST: instance GHC.Classes.Eq Language.Egison.PrettyMath.AST.MathValue
+ Language.Egison.PrettyMath.AST: instance GHC.Show.Show Language.Egison.PrettyMath.AST.MathValue
+ Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathValue (Language.Egison.Math.CAS.SymbolExpr, GHC.Num.Integer.Integer)
+ Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathValue GHC.Num.Integer.Integer
+ Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathValue Language.Egison.Data.EgisonValue
+ Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathValue Language.Egison.Math.CAS.CASTerm
+ Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathValue Language.Egison.Math.CAS.CASValue
+ Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathValue Language.Egison.Math.CAS.SymbolExpr
+ Language.Egison.PrettyMath.AST: instance Language.Egison.PrettyMath.AST.ToMathValue a => Language.Egison.PrettyMath.AST.ToMathValue (Language.Egison.Data.Tensor a)
+ Language.Egison.PrettyMath.AST: toMathValue :: ToMathValue a => a -> MathValue
+ Language.Egison.PrettyMath.AsciiMath: showMathValue :: MathValue -> String
+ Language.Egison.PrettyMath.Latex: showMathValue :: MathValue -> String
+ Language.Egison.PrettyMath.Mathematica: showMathValue :: MathValue -> String
+ Language.Egison.PrettyMath.Maxima: showMathValue :: MathValue -> String
+ Language.Egison.Type.Env: [classParams] :: ClassInfo -> [TyVar]
+ Language.Egison.Type.Env: [instTypes] :: InstanceInfo -> [Type]
+ Language.Egison.Type.Error: ClassMethodShadowWarning :: String -> String -> TypeErrorContext -> TypeWarning
+ Language.Egison.Type.Error: ForwardReferenceWarning :: String -> TypeErrorContext -> TypeWarning
+ Language.Egison.Type.Error: MatcherCoverageWarning :: Type -> [String] -> TypeErrorContext -> TypeWarning
+ Language.Egison.Type.Error: MatcherDataArmsNotExhaustive :: String -> Type -> TypeErrorContext -> TypeError
+ Language.Egison.Type.Error: MatcherNextMatcherWarning :: Type -> String -> TypeErrorContext -> TypeWarning
+ Language.Egison.Type.Error: MissingSignatureConstraint :: String -> [Constraint] -> TypeErrorContext -> TypeError
+ Language.Egison.Type.Error: PatternFunctionLinearityError :: String -> [String] -> [String] -> TypeErrorContext -> TypeError
+ Language.Egison.Type.Error: PatternFunctionParamUnderBranchError :: String -> [String] -> TypeErrorContext -> TypeError
+ Language.Egison.Type.Infer: [cfgMatcherConsistencyWarnings] :: InferConfig -> Bool
+ Language.Egison.Type.Infer: [inferBatchDefNames] :: InferState -> Set String
+ Language.Egison.Type.Infer: [inferCasSubtypeEdges] :: InferState -> SubtypeEnv
+ Language.Egison.Type.Infer: [inferDeferredHoleChecks] :: InferState -> [(Type, HoleCompShape, String, TypeErrorContext)]
+ Language.Egison.Type.Infer: [inferGlobalSubst] :: InferState -> Subst
+ Language.Egison.Type.Infer: [inferInMatcherBody] :: InferState -> Bool
+ Language.Egison.Type.Infer: [inferPatfunParamTaup] :: InferState -> Map String Type
+ Language.Egison.Type.Infer: [inferPatfunTaupEqs] :: InferState -> Maybe [(Type, Type)]
+ Language.Egison.Type.Infer: [inferPatternFuncStructEnv] :: InferState -> PatternTypeEnv
+ Language.Egison.Type.Infer: instance GHC.Show.Show Language.Egison.Type.Infer.HoleCompShape
+ Language.Egison.Type.Instance: AmbiguousIncomparable :: AmbiguityError
+ Language.Egison.Type.Instance: AmbiguousMultipleMaxima :: AmbiguityError
+ Language.Egison.Type.Instance: NoMatchingInstance :: AmbiguityError
+ Language.Egison.Type.Instance: data AmbiguityError
+ Language.Egison.Type.Instance: findMatchingInstanceForTypes :: [Type] -> [InstanceInfo] -> Maybe InstanceInfo
+ Language.Egison.Type.Instance: findMostSpecificInstanceForTypes :: SubtypeEnv -> [Type] -> [InstanceInfo] -> Either AmbiguityError InstanceInfo
+ Language.Egison.Type.Instance: instance GHC.Classes.Eq Language.Egison.Type.Instance.AmbiguityError
+ Language.Egison.Type.Instance: instance GHC.Show.Show Language.Egison.Type.Instance.AmbiguityError
+ Language.Egison.Type.Instance: selectMostSpecific :: SubtypeEnv -> (c -> [Type]) -> [Type] -> [c] -> Either AmbiguityError c
+ Language.Egison.Type.RuntimeType: runtimeTypeOfCAS :: CASValue -> Type
+ Language.Egison.Type.RuntimeType: shallowTypeOfCAS :: CASValue -> Type
+ Language.Egison.Type.Subtype: EdgeAmbiguous :: [(Type, Type, Type)] -> EdgeCheck
+ Language.Egison.Type.Subtype: EdgeCycle :: EdgeCheck
+ Language.Egison.Type.Subtype: EdgeOk :: EdgeCheck
+ Language.Egison.Type.Subtype: EdgeRedundant :: EdgeCheck
+ Language.Egison.Type.Subtype: EdgeRefines :: [(Type, Type, Type)] -> EdgeCheck
+ Language.Egison.Type.Subtype: checkEdgeAddition :: SubtypeEnv -> SubtypeEdge -> EdgeCheck
+ Language.Egison.Type.Subtype: data EdgeCheck
+ Language.Egison.Type.Subtype: instance GHC.Classes.Eq Language.Egison.Type.Subtype.EdgeCheck
+ Language.Egison.Type.Subtype: instance GHC.Show.Show Language.Egison.Type.Subtype.EdgeCheck
+ Language.Egison.Type.Subtype: isCasType :: Type -> Bool
+ Language.Egison.Type.Subtype: isSubtypeWith :: SubtypeEnv -> Type -> Type -> Bool
+ Language.Egison.Type.Subtype: joinTypesWith :: SubtypeEnv -> Type -> Type -> Maybe Type
+ Language.Egison.Type.Subtype: sameCasHead :: Type -> Type -> Bool
+ Language.Egison.Type.Subtype: skeletonJoin :: Type -> Type -> Maybe Type
+ Language.Egison.Type.Subtype: skeletonSubtype :: Type -> Type -> Bool
+ Language.Egison.Type.Subtype: symbolSetSubset :: SymbolSet -> SymbolSet -> Bool
+ Language.Egison.Type.Subtype: type SubtypeEdge = (Type, Type)
+ Language.Egison.Type.Subtype: type SubtypeEnv = [SubtypeEdge]
+ Language.Egison.Type.TypeClassExpand: fixUnboundDictRefs :: ClassEnv -> TIExpr -> TIExpr
+ Language.Egison.Type.Types: SymbolSetClosed :: [TypeAtom] -> SymbolSet
+ Language.Egison.Type.Types: SymbolSetOpen :: SymbolSet
+ Language.Egison.Type.Types: SymbolSetVar :: TyVar -> SymbolSet
+ Language.Egison.Type.Types: TAApplyAtom :: String -> [TypeAtom] -> TypeAtom
+ Language.Egison.Type.Types: TAIntAtom :: Integer -> TypeAtom
+ Language.Egison.Type.Types: TANameAtom :: String -> TypeAtom
+ Language.Egison.Type.Types: TFactor :: Type
+ Language.Egison.Type.Types: TFrac :: Type -> Type
+ Language.Egison.Type.Types: TMatcherSlot :: Type -> Type -> Type
+ Language.Egison.Type.Types: TMathValue :: Type
+ Language.Egison.Type.Types: TPoly :: Type -> SymbolSet -> Type
+ Language.Egison.Type.Types: TTerm :: Type -> SymbolSet -> Type
+ Language.Egison.Type.Types: [classParams] :: ClassInfo -> [TyVar]
+ Language.Egison.Type.Types: [constraintTypes] :: Constraint -> [Type]
+ Language.Egison.Type.Types: [instTypes] :: InstanceInfo -> [Type]
+ Language.Egison.Type.Types: classParam :: ClassInfo -> TyVar
+ Language.Egison.Type.Types: constraintType :: Constraint -> Type
+ Language.Egison.Type.Types: data SymbolSet
+ Language.Egison.Type.Types: data TypeAtom
+ Language.Egison.Type.Types: expandTypeAliases :: HashMap String Type -> Type -> Type
+ Language.Egison.Type.Types: hasAmbiguousOpenTower :: Type -> Bool
+ Language.Egison.Type.Types: instType :: InstanceInfo -> Type
+ Language.Egison.Type.Types: instance Data.Hashable.Class.Hashable Language.Egison.Type.Types.SymbolSet
+ Language.Egison.Type.Types: instance Data.Hashable.Class.Hashable Language.Egison.Type.Types.TypeAtom
+ Language.Egison.Type.Types: instance GHC.Classes.Eq Language.Egison.Type.Types.SymbolSet
+ Language.Egison.Type.Types: instance GHC.Classes.Eq Language.Egison.Type.Types.TypeAtom
+ Language.Egison.Type.Types: instance GHC.Classes.Ord Language.Egison.Type.Types.SymbolSet
+ Language.Egison.Type.Types: instance GHC.Classes.Ord Language.Egison.Type.Types.TypeAtom
+ Language.Egison.Type.Types: instance GHC.Generics.Generic Language.Egison.Type.Types.SymbolSet
+ Language.Egison.Type.Types: instance GHC.Generics.Generic Language.Egison.Type.Types.TypeAtom
+ Language.Egison.Type.Types: instance GHC.Show.Show Language.Egison.Type.Types.SymbolSet
+ Language.Egison.Type.Types: instance GHC.Show.Show Language.Egison.Type.Types.TypeAtom
+ Language.Egison.Type.Types: isCASType :: Type -> Bool
+ Language.Egison.Type.Types: isSubsetSymbolSet :: SymbolSet -> SymbolSet -> Bool
+ Language.Egison.Type.Types: mapType :: (Type -> Type) -> Type -> Type
+ Language.Egison.Type.Types: prettyTypeAtomValue :: TypeAtom -> String
+ Language.Egison.Type.Types: reservedCasTypeNames :: Set String
+ Language.Egison.Type.Types: substTyVar :: TyVar -> Type -> Type -> Type
+ Language.Egison.Type.Types: typeAtomExprToTypeAtom :: TypeAtomExpr -> TypeAtom
+ Language.Egison.Type.Unify: MatcherRigidity :: Type -> Type -> UnifyError
+ Language.Egison.Type.Unify: instance GHC.Classes.Eq Language.Egison.Type.Unify.TensorHandling
+ Language.Egison.Type.Unify: matchOneWay :: Type -> Type -> Maybe Subst
- Language.Egison.CmdOptions: EgisonOpts :: Maybe (String, [String]) -> Bool -> Maybe String -> Maybe String -> [(String, String)] -> [String] -> [String] -> Maybe String -> Maybe String -> Maybe String -> Bool -> Bool -> Bool -> Bool -> Bool -> String -> Maybe String -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> EgisonOpts
+ Language.Egison.CmdOptions: EgisonOpts :: Maybe (String, [String]) -> Bool -> Maybe String -> Maybe String -> [(String, String)] -> [String] -> [String] -> Maybe String -> Maybe String -> Maybe String -> Bool -> Bool -> Bool -> Bool -> Bool -> String -> Maybe String -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> EgisonOpts
- Language.Egison.Data: Env :: [EnvLayer] -> Maybe (String, [Index (Maybe ScalarData)]) -> PatFuncEnv -> Env
+ Language.Egison.Data: Env :: [EnvLayer] -> Maybe (String, [Index (Maybe CASValue)]) -> PatFuncEnv -> Env
- Language.Egison.EnvBuilder: EnvBuildResult :: TypeEnv -> ClassEnv -> ConstructorEnv -> PatternConstructorEnv -> PatternTypeEnv -> EnvBuildResult
+ Language.Egison.EnvBuilder: EnvBuildResult :: TypeEnv -> ClassEnv -> ConstructorEnv -> PatternConstructorEnv -> PatternTypeEnv -> [(Maybe String, RuleLevel, Pattern, Expr)] -> [(String, Expr)] -> Set String -> HashMap String Type -> [(Type, Type)] -> EnvBuildResult
- Language.Egison.EvalState: EvalState :: [Var] -> InstanceEnv -> ConstructorEnv -> TypeEnv -> ClassEnv -> PatternTypeEnv -> PatternTypeEnv -> EvalState
+ Language.Egison.EvalState: EvalState :: [Var] -> InstanceEnv -> ConstructorEnv -> TypeEnv -> ClassEnv -> PatternTypeEnv -> PatternTypeEnv -> PatternTypeEnv -> Int -> Int -> [String] -> [String] -> [String] -> [Set String] -> [String] -> HashMap String Type -> [(Type, Type)] -> [String] -> EvalState
- Language.Egison.PrettyMath.AST: Atom :: String -> [MathIndex] -> MathExpr
+ Language.Egison.PrettyMath.AST: Atom :: String -> [MathIndex] -> MathValue
- Language.Egison.PrettyMath.AST: Collection :: [MathExpr] -> MathExpr
+ Language.Egison.PrettyMath.AST: Collection :: [MathValue] -> MathValue
- Language.Egison.PrettyMath.AST: Div :: MathExpr -> MathExpr -> MathExpr
+ Language.Egison.PrettyMath.AST: Div :: MathValue -> MathValue -> MathValue
- Language.Egison.PrettyMath.AST: Func :: MathExpr -> [MathExpr] -> MathExpr
+ Language.Egison.PrettyMath.AST: Func :: MathValue -> [MathValue] -> MathValue
- Language.Egison.PrettyMath.AST: Multiply :: [MathExpr] -> MathExpr
+ Language.Egison.PrettyMath.AST: Multiply :: [MathValue] -> MathValue
- Language.Egison.PrettyMath.AST: NegativeAtom :: String -> MathExpr
+ Language.Egison.PrettyMath.AST: NegativeAtom :: String -> MathValue
- Language.Egison.PrettyMath.AST: Partial :: MathExpr -> [MathExpr] -> MathExpr
+ Language.Egison.PrettyMath.AST: Partial :: MathValue -> [MathValue] -> MathValue
- Language.Egison.PrettyMath.AST: Plus :: [MathExpr] -> MathExpr
+ Language.Egison.PrettyMath.AST: Plus :: [MathValue] -> MathValue
- Language.Egison.PrettyMath.AST: Power :: MathExpr -> MathExpr -> MathExpr
+ Language.Egison.PrettyMath.AST: Power :: MathValue -> MathValue -> MathValue
- Language.Egison.PrettyMath.AST: Quote :: MathExpr -> MathExpr
+ Language.Egison.PrettyMath.AST: Quote :: MathValue -> MathValue
- Language.Egison.PrettyMath.AST: Sub :: MathExpr -> MathIndex
+ Language.Egison.PrettyMath.AST: Sub :: MathValue -> MathIndex
- Language.Egison.PrettyMath.AST: Super :: MathExpr -> MathIndex
+ Language.Egison.PrettyMath.AST: Super :: MathValue -> MathIndex
- Language.Egison.PrettyMath.AST: Tensor :: [MathExpr] -> [MathIndex] -> MathExpr
+ Language.Egison.PrettyMath.AST: Tensor :: [MathValue] -> [MathIndex] -> MathValue
- Language.Egison.PrettyMath.AST: Tuple :: [MathExpr] -> MathExpr
+ Language.Egison.PrettyMath.AST: Tuple :: [MathValue] -> MathValue
- Language.Egison.PrettyMath.AST: parseExpr :: Parser MathExpr
+ Language.Egison.PrettyMath.AST: parseExpr :: Parser MathValue
- Language.Egison.Type.Env: ClassInfo :: [String] -> TyVar -> [(String, Type)] -> ClassInfo
+ Language.Egison.Type.Env: ClassInfo :: [String] -> [TyVar] -> [(String, Type)] -> ClassInfo
- Language.Egison.Type.Env: InstanceInfo :: [Constraint] -> String -> Type -> [(String, ())] -> InstanceInfo
+ Language.Egison.Type.Env: InstanceInfo :: [Constraint] -> String -> [Type] -> [(String, ())] -> InstanceInfo
- Language.Egison.Type.Infer: InferConfig :: Bool -> Bool -> InferConfig
+ Language.Egison.Type.Infer: InferConfig :: Bool -> Bool -> Bool -> InferConfig
- Language.Egison.Type.Infer: InferState :: Int -> TypeEnv -> [TypeWarning] -> InferConfig -> ClassEnv -> PatternTypeEnv -> PatternTypeEnv -> [Constraint] -> Map String Type -> InferState
+ Language.Egison.Type.Infer: InferState :: Int -> TypeEnv -> [TypeWarning] -> InferConfig -> ClassEnv -> PatternTypeEnv -> PatternTypeEnv -> PatternTypeEnv -> Map String Type -> Maybe [(Type, Type)] -> [Constraint] -> Map String Type -> Bool -> [(Type, HoleCompShape, String, TypeErrorContext)] -> Subst -> SubtypeEnv -> Set String -> InferState
- Language.Egison.Type.Types: ClassInfo :: [String] -> TyVar -> [(String, Type)] -> ClassInfo
+ Language.Egison.Type.Types: ClassInfo :: [String] -> [TyVar] -> [(String, Type)] -> ClassInfo
- Language.Egison.Type.Types: Constraint :: String -> Type -> Constraint
+ Language.Egison.Type.Types: Constraint :: String -> [Type] -> Constraint
- Language.Egison.Type.Types: InstanceInfo :: [Constraint] -> String -> Type -> [(String, ())] -> InstanceInfo
+ Language.Egison.Type.Types: InstanceInfo :: [Constraint] -> String -> [Type] -> [(String, ())] -> InstanceInfo

Files

Changelog.md view
@@ -1,5 +1,56 @@ # Changelog +## 5.1.0++### New Features++* Added an extensible computer-algebra tower with CAS type aliases, subtype+  declarations, runtime dispatch, coercion, and canonical reshaping.+* Added quotient algebras and first-class finite fields, including polynomial+  reduction over declared quotient relations.+* Added multivariate polynomial GCD, Groebner bases, polynomial normal forms,+  and declarative ideals.+* Added user-defined automatic rewrite rules and expanded symbolic+  simplification for radicals, exponentials, and algebraic extensions.+* Added explicit function-symbol metadata, strict differentiation, and unified+  symbolic partial differentiation.+* Added tensor index metadata and higher-order tensor-map lifting.++### Type System and Pattern Matching++* Strengthened matcher consistency and exhaustiveness checking, promoting+  incomplete matcher arms to type errors.+* Improved type-class hierarchy handling, dictionary expansion, instance+  selection, runtime type dispatch, and diagnostics for shadowed class methods.+* Added typing for unannotated top-level recursion.+* Improved inference for tensor callbacks, matcher aliases, structured pattern+  holes, and nonlinear pattern targets.++### Mathematical Libraries++* Added Groebner-basis, ideal, finite-field, algebraic-extension, and interval+  functionality to the standard mathematical libraries.+* Expanded the geometry, algebra, and number-theory samples, including Kahler+  geometry, elliptic curves over finite fields, and CAS-tower examples.+* Improved normalization, matrix and tensor operations, differential forms,+  and root simplification.++### Breaking Changes++* Reorganized the exposed Haskell CAS modules around+  `Language.Egison.Math.CAS`; the former `Math.Arith`, `Math.Expr`, and+  `Math.Normalize` modules are no longer exposed.+* Changed parts of the CAS representation, normalization behavior, symbolic+  function representation, and tensor/matrix APIs. Code relying on Egison 5.0+  implementation details may require updates.++### Testing and Documentation++* Reorganized and expanded the Cabal test suite with CAS, type-error, matcher,+  tensor, quotient-field, and normalization regression tests.+* Added design documentation for the extensible CAS tower, quotient mechanism,+  runtime dispatch, matcher slots, and symbolic simplification.+ ## 5.0.0 ### New Features * **Static Type System**: Introduced a static type system for Egison.
egison.cabal view
@@ -1,5 +1,5 @@ Name:                egison-Version:             5.0.0+Version:             5.1.0 Synopsis:            Programming language with non-linear pattern-matching against non-free data Description:   An interpreter for Egison, a **pattern-matching-oriented**, purely functional programming language@@ -25,6 +25,7 @@ Category:            Compilers/Interpreters Build-type:          Simple Cabal-version:       2.0+Tested-with:         GHC == 9.10.1  Data-files:          lib/core/*.egi                      lib/math/*.egi@@ -38,8 +39,12 @@ Extra-source-files:  README.md                      benchmark/Benchmark.hs                      benchmark/*.egi+                     test/*.egi                      test/lib/math/*.egi                      test/lib/core/*.egi+                     test/type-error/*.egi+                     test/type-error/README.md+                     sample/STATUS.md                      sample/*.egi                      sample/database/*.egi                      sample/io/*.egi@@ -57,6 +62,11 @@   type: git   location: https://github.com/egison/egison.git +source-repository this+  type: git+  location: https://github.com/egison/egison.git+  tag: 5.1.0+ Library   default-language:    GHC2021   Build-Depends:@@ -100,9 +110,7 @@                   Language.Egison.Eval                    Language.Egison.IExpr                    Language.Egison.Match-                   Language.Egison.Math.Arith-                   Language.Egison.Math.Expr-                   Language.Egison.Math.Normalize+                   Language.Egison.Math.CAS                    Language.Egison.Math.Rewrite                    Language.Egison.Math                    Language.Egison.MathOutput@@ -129,6 +137,7 @@                    Language.Egison.Type.Error                    Language.Egison.Type.Index                    Language.Egison.Type.Infer+                   Language.Egison.Type.Subtype                    Language.Egison.Type.Subst                    Language.Egison.Type.TensorMapInsertion                    Language.Egison.Type.TypeClassExpand@@ -138,6 +147,7 @@                    Language.Egison.Type.Unify                    Language.Egison.Type.Instance                    Language.Egison.Type.Pretty+                   Language.Egison.Type.RuntimeType   Other-modules:   Paths_egison   autogen-modules: Paths_egison   ghc-options:  -Wall -Wno-name-shadowing -Wno-incomplete-patterns
hs-src/Interpreter/egison.hs view
@@ -29,8 +29,8 @@ main = execParser cmdParser >>= runWithOptions  isInValidMathOption :: EgisonOpts -> Bool-isInValidMathOption EgisonOpts{ optMathExpr = Just lang } = lang `notElem` ["asciimath", "latex", "mathematica", "maxima", "haskell"]-isInValidMathOption EgisonOpts{ optMathExpr = Nothing }   = False+isInValidMathOption EgisonOpts{ optMathValue = Just lang } = lang `notElem` ["asciimath", "latex", "mathematica", "maxima", "haskell"]+isInValidMathOption EgisonOpts{ optMathValue = Nothing }   = False  runWithOptions :: EgisonOpts -> IO () runWithOptions opts | isInValidMathOption opts =@@ -66,7 +66,7 @@     env <- initialEnv  -- Only primitive environment     evalTopExprs' env allLoadExprs True True   case mResult of-    Left err  -> liftIO $ print err+    Left err  -> liftIO $ print err >> exitFailure     Right (env, evalState) -> handleOptionWithState env evalState opts  handleOption :: Env -> EgisonOpts -> RuntimeM ()@@ -100,8 +100,8 @@       return ()     -- Execute a script from the main function     EgisonOpts { optExecFile = Just (file, args) } -> do-      result <- fromEvalT $ evalTopExprs env [LoadFile file, Execute (makeApply "main" [CollectionExpr (map (ConstantExpr . StringExpr . T.pack) args)])]-      liftIO $ either print (const $ return ()) result+      result <- fromEvalTWithState evalState $ evalTopExprs env [LoadFile file, Execute (makeApply "main" [CollectionExpr (map (ConstantExpr . StringExpr . T.pack) args)])]+      liftIO $ either print (const $ return ()) (fmap fst result)     EgisonOpts { optMapTsvInput = Just expr } ->       handleOption env (opts { optSubstituteString = Just $ "\\x -> map (" ++ expr ++ ") x" })     EgisonOpts { optFilterTsvInput = Just expr } ->
hs-src/Language/Egison.hs view
@@ -22,7 +22,7 @@        , version       ) where -import           Control.Monad.Reader       (asks, local)+import           Control.Monad.Reader       (asks) import           Control.Monad.State  import           Data.Version@@ -69,13 +69,15 @@   , "lib/math/algebra/group.egi"    , "lib/math/common/constants.egi"+  , "lib/math/common/interval.egi"   , "lib/math/common/functions.egi"   , "lib/math/algebra/root.egi"-  , "lib/math/algebra/tensor.egi"    -- Defines (.) (.')+  , "lib/math/algebra/tensor.egi"    -- Defines contractWith, (.) and (.')+  , "lib/math/algebra/matrix.egi"    -- Defines trace, sym and antisym   , "lib/math/algebra/vector.egi"    , "lib/math/algebra/equations.egi"-  , "lib/math/algebra/matrix.egi"+  , "lib/math/algebra/groebner.egi"   , "lib/math/analysis/derivative.egi"    , "lib/math/geometry/differential-form.egi"
hs-src/Language/Egison/AST.hs view
@@ -9,6 +9,7 @@  module Language.Egison.AST   ( TopExpr (..)+  , RuleLevel (..)   , ConstantExpr (..)   , Expr (..)   , Pattern (..)@@ -35,6 +36,8 @@   , extractNameFromVarWithIndices   -- Type annotations   , TypeExpr (..)+  , SymbolSetExpr (..)+  , TypeAtomExpr (..)   , TensorShapeExpr (..)   , ShapeDim (..)   , TensorIndexExpr (..)@@ -89,8 +92,73 @@     -- e.g., declare symbol a11, a12, a21, a22     --       declare symbol x, y, z : Float     -- [String]: symbol names, Maybe TypeExpr: optional type (defaults to Integer)+  | DeclareIdeal [Expr]+    -- ^ Ideal declaration (G3 of design/cas-simplification.md).+    -- e.g.  declare ideal [w^2 + w + 1]+    -- Computes the reduced Groebner basis of the generators once (lazily,+    -- via the Egison-level engine in lib/math/algebra/groebner.egi) and+    -- registers the whole basis as one term-level auto rewrite rule.+  | DeclareRule (Maybe String) RuleLevel Pattern Expr+    -- ^ Reduction rule declaration (Phase 7.4 of type-cas design).+    -- e.g.  declare rule auto term i^2 = -1+    --       declare rule trig_pythagorean poly (sin $x)^2 + (cos #x)^2 = 1+    -- Maybe String:  rule name (Nothing = auto rule)+    -- RuleLevel:     where the LHS pattern binds (term/poly/frac)+    -- Pattern Expr:  LHS pattern (with $x/#x), RHS expression+  | DeclareDerivative String Expr+    -- ^ Derivative declaration (Phase 6.3 of type-cas design).+    -- e.g.  declare derivative sin = cos+    --       declare derivative log = \x -> 1 / x+    -- String: name of the function whose derivative is being declared+    --         (typically introduced by `declare mathfunc` first).+    -- Expr:   the derivative-as-an-expression (typically a function or lambda)+  | DeclareMathFunc String (Maybe TypeExpr)+    -- ^ Math function declaration (Phase 6.3 part 5).+    -- e.g.  declare mathfunc sin+    --       declare mathfunc sqrt : MathValue -> MathValue+    -- Desugars to a wrapper function that quotes the symbol:+    --   def <name> (x : MathValue) : MathValue := '<name> x+    -- Combined with `declare derivative`, this gives the user a callable+    -- function and a registered derivative under one umbrella.+  | DeclareCasType String TypeExpr+    -- ^ Transparent CAS type alias (Phase alpha of the extensible tower;+    -- design/type-cas-tower.md D3: transparent aliases only, no nominal types).+    -- e.g.  declare cas-type GaussianInt := Poly Integer [i]+    -- String: alias name (must be capitalized), TypeExpr: the aliased type.+    -- Expanded away during environment building / desugaring; no runtime artifact.+  | DeclareCasSubtype TypeExpr TypeExpr+    -- ^ Subtype-order edge declaration (Phase beta; design D1/D5).+    -- e.g.  declare cas-subtype Poly Integer [i, x] <: Poly (Poly Integer [i]) [x]+    --       declare cas-subtype Integer ⊂ GaussianInt+    -- Relation only (D5: no embed clause — promotion is always casReshapeAs).+    -- Checked at declare time for the D1 join-semilattice invariant.+  | DeclareCasQuotient String TypeExpr Expr+    -- ^ Coefficient-domain quotient declaration (M4; design/type-cas-quotient.md).+    -- e.g.  declare cas-quotient Mod7 := Integer by (\n -> modulo n 7)+    -- String: nominal quotient type name; TypeExpr: base type; Expr: the+    -- idempotent reduce (representative selector). Macro-expanded before+    -- environment building into reduce<Q>/proj<Q>/repr<Q> definitions,+    -- homomorphic Ring/Eq instances, and congruence-law assertions.+  | DeclareApply String [String] Expr+    -- ^ Math function application rule (Phase A of declare apply impl).+    -- e.g.  declare apply sin x := if x = 0 then 0 else 'sin x+    --       declare apply sqrt x := ...+    -- String:    function name (must have been declared via `declare mathfunc`).+    -- [String]:  argument names (typed as MathValue by default).+    -- Expr:      body. Within body, `'<name> x` produces the symbolic Factor+    --            (no recursion); `<name> x` (unquoted) recurses through this+    --            rule again — RHS must use `'` for the fallback to terminate.+    -- Phase A desugars to a plain `def <name> (args : MathValue ...) : MathValue := <body>`+    -- which simply overrides the wrapper from `declare mathfunc`.  deriving Show +-- | Where in the CASValue tree a `declare rule` LHS pattern binds.+data RuleLevel+  = TermRuleLevel  -- ^ inside a CASTerm monomial (e.g. i^2 = -1)+  | PolyRuleLevel  -- ^ inside a CASPoly term-list  (e.g. (sin x)^2 + (cos x)^2 = 1)+  | FracRuleLevel  -- ^ on a CASFrac  numerator/denominator+  deriving (Show, Eq)+ -- | Type class declaration -- e.g., class Eq a where ... --       class Eq a => Ord a where ...@@ -224,6 +292,7 @@   | FunctionExpr [String]    | TypeAnnotation Expr TypeExpr  -- ^ Expression with type annotation (expr : type)+  | SimplifyUsingExpr Expr String -- ^ Phase 7.6: `simplify <expr> using <ruleName>`. Skeleton: parser only — runtime semantics will appear when the rule-application engine lands.   deriving Show  data VarWithIndices = VarWithIndices String [VarIndex]@@ -327,20 +396,20 @@   | PDConsPat (PDPatternBase var) (PDPatternBase var)   | PDSnocPat (PDPatternBase var) (PDPatternBase var)   | PDConstantPat ConstantExpr-  -- ScalarData (MathExpr) primitive patterns-  | PDDivPat (PDPatternBase var) (PDPatternBase var)        -- Div: ScalarData -> PolyExpr, PolyExpr+  -- MathValue primitive patterns+  | PDFracPat (PDPatternBase var) (PDPatternBase var)        -- Frac: MathValue -> PolyExpr, PolyExpr   | PDPlusPat (PDPatternBase var)                           -- Plus: PolyExpr -> [TermExpr]   | PDTermPat (PDPatternBase var) (PDPatternBase var)       -- Term: TermExpr -> Integer, [(SymbolExpr, Integer)]   | PDSymbolPat (PDPatternBase var) (PDPatternBase var)     -- Symbol: SymbolExpr -> String, [IndexExpr]-  | PDApply1Pat (PDPatternBase var) (PDPatternBase var)     -- Apply1: SymbolExpr -> MathExpr, MathExpr+  | PDApply1Pat (PDPatternBase var) (PDPatternBase var)     -- Apply1: SymbolExpr -> MathValue, MathValue   | PDApply2Pat (PDPatternBase var) (PDPatternBase var) (PDPatternBase var) -- Apply2   | PDApply3Pat (PDPatternBase var) (PDPatternBase var) (PDPatternBase var) (PDPatternBase var) -- Apply3   | PDApply4Pat (PDPatternBase var) (PDPatternBase var) (PDPatternBase var) (PDPatternBase var) (PDPatternBase var) -- Apply4-  | PDQuotePat (PDPatternBase var)                          -- Quote: SymbolExpr -> MathExpr-  | PDFunctionPat (PDPatternBase var) (PDPatternBase var) -- Function: SymbolExpr -> MathExpr, [MathExpr]-  | PDSubPat (PDPatternBase var)                            -- Sub: IndexExpr -> MathExpr-  | PDSupPat (PDPatternBase var)                            -- Sup: IndexExpr -> MathExpr-  | PDUserPat (PDPatternBase var)                           -- User: IndexExpr -> MathExpr+  | PDQuotePat (PDPatternBase var)                          -- Quote: SymbolExpr -> MathValue+  | PDFunctionPat (PDPatternBase var) (PDPatternBase var) -- Function: SymbolExpr -> MathValue, [MathValue]+  | PDSubPat (PDPatternBase var)                            -- Sub: IndexExpr -> MathValue+  | PDSupPat (PDPatternBase var)                            -- Sup: IndexExpr -> MathValue+  | PDUserPat (PDPatternBase var)                           -- User: IndexExpr -> MathValue   deriving (Functor, Foldable, Show)  type PrimitiveDataPattern = PDPatternBase String@@ -403,6 +472,7 @@   [ Op "++" 5 InfixR False   , Op "*:" 5 InfixL False   , Op "+" 7 InfixR False+  , Op "-" 7 InfixL False  -- subtraction in rule LHS, e.g. `1 - $x`   , Op "*" 8 InfixR False   , Op "/" 8 InfixN False   , Op "^" 9 InfixN False@@ -429,8 +499,8 @@  -- | Type expression in source code data TypeExpr-  = TEInt                              -- ^ Integer (= MathExpr)-  | TEMathExpr                         -- ^ MathExpr (= Integer)+  = TEInt                              -- ^ Integer (= MathValue)+  | TEMathValue                         -- ^ MathValue (= Integer)   | TEFloat                            -- ^ Float   | TEBool                             -- ^ Bool   | TEChar                             -- ^ Char@@ -440,6 +510,7 @@   | TETuple [TypeExpr]                 -- ^ Tuple type, e.g., (a, b)   | TEFun TypeExpr TypeExpr            -- ^ Function type, e.g., a -> b   | TEMatcher TypeExpr                 -- ^ Matcher type+  | TEMatcherSlot TypeExpr TypeExpr    -- ^ MatcherSlot type, e.g., MatcherSlot a a (structural / target)   | TEPattern TypeExpr                 -- ^ Pattern type, e.g., Pattern a   | TEIO TypeExpr                      -- ^ IO type, e.g., IO ()   | TETensor TypeExpr                  -- ^ Tensor type, e.g., Tensor a@@ -449,7 +520,28 @@   | TEApp TypeExpr [TypeExpr]          -- ^ Type application, e.g., List a   | TEConstrained [ConstraintExpr] TypeExpr                                       -- ^ Constrained type, e.g., Eq a => a+  -- New CAS types (Phase 2)+  | TEFactor                           -- ^ Factor type (atomic mathematical factor)+  | TETerm TypeExpr SymbolSetExpr      -- ^ Term type, e.g., Term Integer [x] (single monomial over the given atoms)+  | TEFrac TypeExpr                     -- ^ Frac type, e.g., Frac Integer+  | TEPoly TypeExpr SymbolSetExpr      -- ^ Poly type, e.g., Poly Integer [x, y]   deriving (Show, Eq)++-- | Symbol set expression for polynomial types+data SymbolSetExpr+  = SSEClosed [TypeAtomExpr]            -- ^ Fixed symbol set, e.g., [x, y, sqrt 2]+  | SSEOpen                             -- ^ Open symbol set, [..]+  deriving (Show, Eq)++-- | A single atom inside a closed symbol set: either a plain identifier+-- (`x`, `i`), or a function applied to atom arguments (`sqrt 2`, `sin x`).+-- This is the AST level (parser output); the Type level uses a similar+-- structure (`TypeAtom` in Type.Types).+data TypeAtomExpr+  = TAEName String                      -- ^ Plain symbol/identifier+  | TAEApp String [TypeAtomExpr]        -- ^ Function applied to atom arguments+  | TAEInt Integer                      -- ^ Integer literal in atom position+  deriving (Show, Eq, Ord)  -- | Tensor shape expression data TensorShapeExpr
hs-src/Language/Egison/CmdOptions.hs view
@@ -34,7 +34,7 @@     optShowBanner       :: Bool,     optTestOnly         :: Bool,     optPrompt           :: String,-    optMathExpr         :: Maybe String,+    optMathValue         :: Maybe String,     optMathNormalize    :: Bool,     optTypeCheck        :: Bool,       -- ^ Enable type checking     optTypeCheckStrict  :: Bool,       -- ^ Strict type checking mode@@ -42,11 +42,12 @@     optDumpDesugared    :: Bool,       -- ^ Dump desugared AST after Phase 3     optDumpTyped        :: Bool,       -- ^ Dump typed AST after Phase 6 (type inference & check)     optDumpTi           :: Bool,       -- ^ Dump typed AST after TensorMap insertion (before type class expansion)-    optDumpTc           :: Bool        -- ^ Dump typed AST after type class expansion (Phase 8 complete)+    optDumpTc           :: Bool,       -- ^ Dump typed AST after type class expansion (Phase 7 complete)+    optMatcherConsistencyWarnings :: Bool        -- ^ Emit matcher consistency warnings (paper Def 4.2: Coverage 4.2(3) + PP-Con 4.2(1a)); arm exhaustiveness (4.2(1c)) is an ordinary type error, not gated here     }  defaultOption :: EgisonOpts-defaultOption = EgisonOpts Nothing False Nothing Nothing [] [] [] Nothing Nothing Nothing False False False True False "> " Nothing True True False False False False False False+defaultOption = EgisonOpts Nothing False Nothing Nothing [] [] [] Nothing Nothing Nothing False False False True False "> " Nothing True True False False False False False False False --                                                                                                     ^^^^^ optNoPrelude --                                                                                                                                      ^^^^ optTypeCheck is now True by default --                                                                                                                                              ^^^^^ optDumpEnv@@ -156,7 +157,10 @@                   <> help "Dump typed AST after TensorMap insertion (before type class expansion)")             <*> switch                   (long "dump-tc"-                  <> help "Dump typed AST after type class expansion (Phase 8 complete)")+                  <> help "Dump typed AST after type class expansion (Phase 7 complete)")+            <*> switch+                  (long "matcher-consistency-warnings"+                  <> help "Emit matcher consistency warnings (paper Def 4.2): Coverage (4.2(3)) — a matcher lacking a general clause for some pattern constructor of its matched type; and PP-Con (4.2(1a)) — a bare-variable matcher `something` at a constructor-headed next-matcher hole. Arm exhaustiveness (4.2(1c)) is an ordinary type error, not gated by this flag")  readFieldOption :: ReadM (String, String) readFieldOption = eitherReader $ \str ->
hs-src/Language/Egison/Core.hs view
@@ -7,10 +7,10 @@ Module      : Language.Egison.Core Licence     : MIT -This module implements Phase 10: Evaluation.+This module implements Phase 9: Evaluation. It provides functions to evaluate expressions and perform pattern matching. -Evaluation Phase (Phase 10):+Evaluation Phase (Phase 9):   - Pattern matching execution (patternMatch function)     * Egison's powerful non-linear pattern matching with backtracking     * Pattern matching is NOT desugared but executed during evaluation@@ -38,6 +38,9 @@     , recursiveBindPatFuncs     , recursiveBindAll     , makeBindings'+    -- * Function application (used by primitives that take user closures)+    , applyRef+    , applyObj     -- * Pattern matching     , patternMatch     ) where@@ -50,18 +53,16 @@ import           Control.Monad.State import           Control.Monad.Trans.Maybe -import           Data.Char                       (isUpper)+import           Data.Char                       (isUpper, toLower) import           Data.Foldable                   (toList) import           Data.IORef-import           Data.List                       (partition)+import           Data.List                       (partition, sortOn) import           Data.Maybe import qualified Data.Sequence                   as Sq import           Data.Traversable                (mapM)  import qualified Data.HashMap.Lazy               as HL-import qualified Data.HashMap.Strict             as HashMap import qualified Data.Vector                     as V-import           Data.Text                       (Text) import qualified Data.Text                       as T  import           Language.Egison.Data@@ -71,18 +72,19 @@ import           Language.Egison.IExpr import           Language.Egison.MList import           Language.Egison.Match-import           Language.Egison.Math+import qualified Language.Egison.Math.CAS as CAS import           Language.Egison.RState import           Language.Egison.Tensor import           Language.Egison.Type.Types      (Type(..))+import qualified Language.Egison.Type.RuntimeType as RT+import           Language.Egison.Type.Instance   (selectMostSpecific)+import           Language.Egison.Type.Subtype    (SubtypeEnv, isSubtypeWith, isCasType)  -- | Get the Type of an EgisonValue -- Used for type class method dispatch valueToType :: EgisonValue -> Type valueToType (Bool _)         = TBool-valueToType (ScalarData (Div (Plus []) (Plus [Term 1 []])))          = TInt-valueToType (ScalarData (Div (Plus [Term _ []]) (Plus [Term 1 []]))) = TInt-valueToType (ScalarData _)   = TInt  -- MathExpr = TInt in Egison+valueToType (CASData _)      = TInt  -- MathValue = TInt in Egison valueToType (Float _)        = TFloat valueToType (Char _)         = TChar valueToType (String _)       = TString@@ -126,8 +128,8 @@ evalExprShallow env (IQuoteExpr expr) = do   whnf <- evalExprShallow env expr   case whnf of-    Value (ScalarData s) -> return . Value . ScalarData $ SingleTerm 1 [(Quote s, 1)]-    _                    -> throwErrorWithTrace (TypeMismatch "scalar in quote" whnf)+    Value (CASData cv) -> return $ Value (quoteCASData cv)+    _                  -> throwErrorWithTrace (TypeMismatch "scalar in quote" whnf)  evalExprShallow env (IQuoteSymbolExpr expr) =   case expr of@@ -137,26 +139,26 @@         Just ref -> do           val <- evalRef ref           case val of-            Value func@(Func _ _ _ _) -> +            Value (Func _ _ _ _) ->               -- Quote the function object itself-              return . Value . ScalarData $ SingleTerm 1 [(QuoteFunction val, 1)]-            Value func@(MemoizedFunc _ _ _ _) -> +              return $ Value (quoteFunctionCASData val)+            Value (MemoizedFunc _ _ _ _) ->               -- Quote the memoized function object itself-              return . Value . ScalarData $ SingleTerm 1 [(QuoteFunction val, 1)]-            Value (ScalarData _) -> return val-            _ -> return . Value $ symbolScalarData "" name-        Nothing -> return . Value $ symbolScalarData "" name+              return $ Value (quoteFunctionCASData val)+            Value (CASData _) -> return val+            _ -> return $ Value (symbolCASData "" name)+        Nothing -> return $ Value (symbolCASData "" name)     _ -> do       whnf <- evalExprShallow env expr       case whnf of-        Value (ScalarData _) -> return whnf-        _                    -> throwErrorWithTrace (TypeMismatch "scalar or symbol in quote-symbol" whnf)+        Value (CASData _) -> return whnf+        _                 -> throwErrorWithTrace (TypeMismatch "scalar or symbol in quote-symbol" whnf)  evalExprShallow env (IVarExpr name) =   case refVar env (Var name []) of-    Nothing | isUpper (head name) ->+    Nothing | (c:_) <- name, isUpper c ->       return $ Value (InductiveData name [])-    Nothing  -> return $ Value (symbolScalarData "" name)+    Nothing  -> return $ Value (symbolCASData "" name)     Just ref -> evalRef ref  evalExprShallow _ (ITupleExpr []) = return . Value $ Tuple []  -- Unit value ()@@ -224,10 +226,10 @@   makeHashKey :: WHNFData -> EvalM EgisonHashKey   makeHashKey (Value val) =     case val of-      ScalarData _ -> IntKey <$> fromEgison val-      Char c       -> return (CharKey c)-      String str   -> return (StrKey str)-      _            -> throwErrorWithTrace (TypeMismatch "integer or string" (Value val))+      CASData _ -> IntKey <$> fromEgison val+      Char c    -> return (CharKey c)+      String str-> return (StrKey str)+      _         -> throwErrorWithTrace (TypeMismatch "integer or string" (Value val))   makeHashKey whnf = throwErrorWithTrace (TypeMismatch "integer or string" whnf)  evalExprShallow env@(Env _fs _ _) (IIndexedExpr override expr indices) = do@@ -240,14 +242,12 @@                   Nothing     -> evalExprShallow env expr               _ -> evalExprShallow env expr   case whnf of-    Value (ScalarData (SingleTerm 1 [(Symbol id name js', 1)])) -> do-      js2 <- mapM evalIndexToScalar indices-      return $ Value (ScalarData (SingleTerm 1 [(Symbol id name (js' ++ js2), 1)]))+    Value (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name js', 1)]])) -> do+      js2 <- mapM evalIndexToCAS indices+      return $ Value $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (js' ++ js2), 1)]]     Value (Func v@(Just (Var _fnName is)) env args body) -> do       js <- mapM evalIndex indices-      liftIO $ putStrLn $ "[DEBUG pmIndices] is: " ++ show is ++ ", js: " ++ show js       frame <- pmIndices is js-      liftIO $ putStrLn $ "can reach here"       let env' = extendEnv env frame       return $ Value (Func v env' args body)     Value (TensorData t@Tensor{}) -> do@@ -263,9 +263,13 @@   evalIndex :: Index IExpr -> EvalM (Index EgisonValue)   evalIndex index = traverse (evalExprDeep env) index -  evalIndexToScalar :: Index IExpr -> EvalM (Index ScalarData)-  evalIndexToScalar index = traverse ((extractScalar =<<) . evalExprDeep env) index+  evalIndexToCAS :: Index IExpr -> EvalM (Index CASValue)+  evalIndexToCAS index = traverse (evalExprDeep env >=> extractCASValue) index +  extractCASValue :: EgisonValue -> EvalM CASValue+  extractCASValue (CASData cv) = return cv+  extractCASValue val = throwErrorWithTrace (TypeMismatch "CASData" (Value val))+ evalExprShallow env (ISubrefsExpr override expr jsExpr) = do   js <- map Sub <$> (evalExprDeep env jsExpr >>= collectionToList)   tensor <- case expr of@@ -276,13 +280,13 @@                   Nothing     -> evalExprShallow env expr               _ -> evalExprShallow env expr   case tensor of-    Value (ScalarData _)          -> return tensor-    Value (TensorData t@Tensor{}) -> Value <$> refTensorWithOverride override js t-    ITensor t@Tensor{}            -> refTensorWithOverride override js t+    Value (CASData _)               -> return tensor+    Value (TensorData t@Tensor{})   -> Value <$> refTensorWithOverride override js t+    ITensor t@Tensor{}              -> refTensorWithOverride override js t     _ -> do       val <- evalWHNF tensor       case val of-        ScalarData _          -> return $ Value val+        CASData _             -> return $ Value val         TensorData t@Tensor{} -> Value <$> refTensorWithOverride override js t         _                     -> throwErrorWithTrace (NotImplemented ("subrefs for " ++ show val)) @@ -296,30 +300,77 @@                   Nothing     -> evalExprShallow env expr               _ -> evalExprShallow env expr   case tensor of-    Value (ScalarData _)          -> return tensor-    Value (TensorData t@Tensor{}) -> Value <$> refTensorWithOverride override js t-    ITensor t@Tensor{}            -> refTensorWithOverride override js t+    Value (CASData _)               -> return tensor+    Value (TensorData t@Tensor{})   -> Value <$> refTensorWithOverride override js t+    ITensor t@Tensor{}              -> refTensorWithOverride override js t     _ -> do       val <- evalWHNF tensor       case val of-        ScalarData _          -> return $ Value val+        CASData _             -> return $ Value val         TensorData t@Tensor{} -> Value <$> refTensorWithOverride override js t         _                     -> throwErrorWithTrace (NotImplemented ("suprefs for " ++ show val))  evalExprShallow env (IUserrefsExpr _ expr jsExpr) = do   val <- evalExprDeep env expr-  js <- map User <$> (evalExprDeep env jsExpr >>= collectionToList >>= mapM extractScalar)+  jsRaw <- evalExprDeep env jsExpr >>= collectionToList >>= mapM extractCASVal+  let jsCAS = map User jsRaw   case val of-    ScalarData (SingleTerm 1 [(Symbol id name is, 1)]) ->-      return $ Value (ScalarData (SingleTerm 1 [(Symbol id name (is ++ js), 1)]))-    ScalarData (SingleTerm 1 [(FunctionData sym args, 1)]) ->+    -- A bare symbol takes user indices verbatim (indexed-symbol feature,+    -- unrelated to derivative marks).+    CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]]) ->+      return $ Value $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (is ++ jsCAS), 1)]]+    -- On a function symbol the user indices are DERIVATIVE MARKS, and are+    -- normalized at construction: an argument value resolves to its+    -- position (the arguments are values, not names, so a positional+    -- multi-index is the only well-defined form), positions are+    -- range-checked, and the combined multi-index is kept sorted --+    -- mixed partials of the smooth unknown functions these stand for+    -- commute (Schwarz), so f|2|1 and f|1|2 must be the same atom.+    CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData sym args, 1)]]) ->       case sym of-        SingleTerm 1 [(Symbol id name is, 1)] -> do-          let sym' = SingleTerm 1 [(Symbol id name (is ++ js), 1)]-          return $ Value (ScalarData (SingleTerm 1 [(FunctionData sym' args, 1)]))+        CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]] -> do+          posIdxs <- mapM (resolveFnIndex name args) jsRaw+          let (users, others) = partition isUserIndex is+              users' = sortOn userKey (users ++ map (User . CASInteger) posIdxs)+              sym' = CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (others ++ users'), 1)]]+          return $ Value $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData sym' args, 1)]]         _ -> throwErrorWithTrace (NotImplemented "user-refs")     _ -> throwErrorWithTrace (NotImplemented "user-refs")+ where+  extractCASVal :: EgisonValue -> EvalM CASValue+  extractCASVal (CASData cv) = return cv+  extractCASVal v = throwErrorWithTrace (TypeMismatch "CASData" (Value v)) +  resolveFnIndex :: String -> [CASValue] -> CASValue -> EvalM Integer+  resolveFnIndex name args j = case casToSmallInt j of+    Just n+      | 1 <= n && n <= fromIntegral (length args) -> return n+      | otherwise -> throwError $ Default $+          "userRefs: index " ++ show n ++ " is out of range for the "+          ++ show (length args) ++ "-argument function symbol " ++ name+    Nothing -> case [i | (i, a) <- zip [1..] args, a == j] of+      [i] -> return i+      []  -> throwError $ Default $+          "userRefs: " ++ CAS.prettyCAS j ++ " is not an argument of the function symbol " ++ name+      _   -> throwError $ Default $+          "userRefs: " ++ CAS.prettyCAS j ++ " appears more than once among the arguments of "+          ++ name ++ "; use a positional index"++  casToSmallInt :: CASValue -> Maybe Integer+  casToSmallInt (CASInteger n) = Just n+  casToSmallInt (CASPoly [CASTerm (CASInteger n) []]) = Just n+  casToSmallInt (CASFrac num (CASInteger 1)) = casToSmallInt num+  casToSmallInt (CASFrac num (CASPoly [CASTerm (CASInteger 1) []])) = casToSmallInt num+  casToSmallInt _ = Nothing++  isUserIndex :: Index a -> Bool+  isUserIndex (User _) = True+  isUserIndex _        = False++  userKey :: Index CASValue -> Integer+  userKey (User v) = fromMaybe (toInteger (maxBound :: Int)) (casToSmallInt v)+  userKey _        = toInteger (maxBound :: Int)+ evalExprShallow env (ILambdaExpr vwi names expr) = do   return . Value $ Func vwi env names expr @@ -332,12 +383,16 @@ evalExprShallow (Env _ Nothing _) (IFunctionExpr _) = throwError $ Default "function symbol is not bound to a variable"  evalExprShallow env@(Env _ (Just (name, is)) _) (IFunctionExpr args) = do-  args' <- mapM (evalExprDeep env . IVarExpr) args >>= mapM extractScalar+  args' <- mapM (evalExprDeep env . IVarExpr) args >>= mapM extractCASVal   is' <- mapM unwrapMaybeFromIndex is-  return . Value $ ScalarData (SingleTerm 1 [(FunctionData (SingleTerm 1 [(Symbol "" name is', 1)]) args', 1)])+  let sym = CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol "" name is', 1)]]+  return $ Value $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData sym args', 1)]]  where-  unwrapMaybeFromIndex :: Index (Maybe ScalarData) -> EvalM (Index ScalarData) -- Maybe we can refactor this function---  unwrapMaybeFromIndex = return . (fmap fromJust)+  extractCASVal :: EgisonValue -> EvalM CASValue+  extractCASVal (CASData cv) = return cv+  extractCASVal v = throwErrorWithTrace (TypeMismatch "CASData" (Value v))++  unwrapMaybeFromIndex :: Index (Maybe CASValue) -> EvalM (Index CASValue)   unwrapMaybeFromIndex (Sub Nothing) = throwError $ Default "function symbol can be used only with generateTensor"   unwrapMaybeFromIndex (Sup Nothing) = throwError $ Default "function symbol can be used only with generateTensor"   unwrapMaybeFromIndex (Sub (Just i)) = return (Sub i)@@ -379,7 +434,7 @@  evalExprShallow env (IWithSymbolsExpr vars expr) = do   symId <- fresh-  syms <- mapM (newEvaluatedObjectRef . Value . symbolScalarData symId) vars+  syms <- mapM (newEvaluatedObjectRef . Value . symbolCASData symId) vars   whnf <- evalExprShallow (extendEnv env (makeBindings' vars syms)) expr   case whnf of     Value (TensorData t@Tensor{}) -> Value . TensorData <$> removeTmpScripts symId t@@ -490,17 +545,27 @@ evalExprShallow env (IGenerateTensorExpr fnExpr shapeExpr) = do   shape <- evalExprDeep env shapeExpr >>= collectionToList   ns    <- mapM fromEgison shape :: EvalM Shape-  xs    <- mapM (evalWithIndex env . map (\n -> SingleTerm n [])) (enumTensorIndices ns)-  return $ newITensor ns xs+  xs    <- mapM (evalWithIndex env . map CASInteger) (enumTensorIndices ns)+  newITensor ns xs  where-  evalWithIndex :: Env -> [ScalarData] {- index -} -> EvalM ObjectRef+  evalWithIndex :: Env -> [CASValue] {- index -} -> EvalM ObjectRef   evalWithIndex env@(Env frame maybe_vwi pfEnv) ms = do-    let env' = maybe env (\(name, indices) -> Env frame (Just (name, zipWith changeIndex indices ms)) pfEnv) maybe_vwi+    let env' = maybe env (\(name, indices) ->+          -- Omitted tensor axes are covariant by default.  Complete the+          -- definition context so function-symbol components of a bare+          -- tensor binding still receive their component-position names.+          -- Preserve positions completed by outer generateTensor calls.+          Env frame (Just (name, fillIndices indices ms)) pfEnv) maybe_vwi     fn <- evalExprShallow env' fnExpr-    newApplyObjThunkRef env fn [WHNF (Value (Collection (Sq.fromList (map ScalarData ms))))]-  changeIndex :: Index (Maybe a) -> a -> Index (Maybe a) -- Maybe we can refactor this function-  changeIndex (Sup Nothing) m = Sup (Just m)-  changeIndex (Sub Nothing) m = Sub (Just m)+    newApplyObjThunkRef env fn [WHNF (Value (Collection (Sq.fromList (map CASData ms))))]+  fillIndices :: [Index (Maybe a)] -> [a] -> [Index (Maybe a)]+  fillIndices indices [] = indices+  fillIndices [] ms = map (Sub . Just) ms+  fillIndices (Sup Nothing : indices) (m : ms) =+    Sup (Just m) : fillIndices indices ms+  fillIndices (Sub Nothing : indices) (m : ms) =+    Sub (Just m) : fillIndices indices ms+  fillIndices (index : indices) ms = index : fillIndices indices ms  evalExprShallow env (ITensorContractExpr tExpr) = do   whnf <- evalExprShallow env tExpr@@ -588,6 +653,89 @@   -- Create a PatternFunc value, capturing the current environment   return $ Value (PatternFunc env paramNames body) +-- Reshape (Phase A of design/type-cas-implementation-status.md §reshape).+-- Inserted by post-typecheck elaboration from a type annotation. At eval time+-- we structurally rewrite the inner CAS value to fit the annotation's type.+-- Non-CAS values pass through unchanged.+evalExprShallow env (IReshape ty inner) = do+  whnf <- evalExprShallow env inner+  case whnf of+    Value (CASData cv) -> return $ Value $ CASData (CAS.casReshapeAs ty cv)+    _                  -> return whnf++-- Runtime-type dispatch (Phase 3 of design/runtime-type-dispatch.md).+-- TypeClassExpand emits this node when a type-class method is called on a+-- value whose static type is `MathValue` and no explicit+-- `instance Class MathValue` exists. We:+--   1. evaluate the first argument to get the CAS value+--   2. compute its shallow runtime type+--   3. pick the dictionary for the most specific candidate+--   4. construct the equivalent dictionary-indexed application and evaluate.+evalExprShallow env (IRuntimeDispatch className methodName candidates args) = do+  case args of+    [] ->+      throwError $ Default $+        "runtime dispatch: no arguments for " ++ className ++ "." ++ methodName+    (firstArg : restArgs) -> do+      -- Evaluate the first argument exactly once. We cannot re-emit+      -- `IApplyExpr (IIndexedExpr (IVarExpr dictName)) args` here because+      -- `IApplyExpr` re-thunks every IExpr arg, which would re-evaluate+      -- `firstArg` from scratch (defeating the work we already did to+      -- compute its CAS shape). For deeply nested recursive partialDiff+      -- calls (e.g. tensor-Christoffel), that doubling compounds and+      -- causes orders-of-magnitude slowdown. So we reuse the WHNFData by+      -- wrapping it in an evaluated ObjectRef and call `applyRef`+      -- directly with cached ref + thunked rest args.+      firstWhnf <- evalExprShallow env firstArg+      cv <- case firstWhnf of+        Value (CASData c) -> return c+        _ -> throwErrorWithTrace+               (TypeMismatch ("CASData (for " ++ className ++ "." ++ methodName ++ " runtime dispatch)") firstWhnf)+      let rt = RT.runtimeTypeOfCAS cv+      edges <- getCasSubtypeEdges+      -- The candidate list omits the class's MathValue instance (the+      -- expander's self-selection guard), so a value whose runtime type+      -- is a plain CAS shape -- e.g. a let-generalized combination that+      -- collapses to the integer 0 at some call site -- may match no+      -- candidate.  Fall back to the MathValue dictionary then: call+      -- sites inside that instance are typed TMathValue and never emit+      -- a dispatch node, so this cannot loop.+      let mvDict = mvDictName className+          runDict dictName = do+            dictWhnf   <- evalExprShallow env (IVarExpr dictName)+            methodWhnf <- refHash dictWhnf [String (T.pack methodName)]+            firstRef   <- newEvaluatedObjectRef firstWhnf+            restRefs   <- mapM (newThunkRef env) restArgs+            applyRef env methodWhnf (firstRef : restRefs) >>= removeDF+      case findBestRuntimeCandidate edges rt candidates of+        Just dictName -> runDict dictName+        Nothing+          | isCasType rt, isJust (refVar env (stringToVar mvDict)) ->+              runDict mvDict+        Nothing ->+          throwError $ Default $+            "runtime dispatch: no matching instance for "+              ++ className ++ " on value of runtime type " ++ show rt+  where+    -- Pick the dictionary name whose instance type is the most specific+    -- supertype of `target`, in the declared CAS order (skeleton ++    -- `declare cas-subtype` edges). Candidate filtering is by plain+    -- subtyping (runtime types are concrete); selection is the shared+    -- `Type.Instance.selectMostSpecific`.+    findBestRuntimeCandidate :: SubtypeEnv -> Type -> [(Type, String)] -> Maybe String+    findBestRuntimeCandidate edges target cands =+      case selectMostSpecific edges (\(t, _) -> [t]) [target]+             (filter (\(t, _) -> isSubtypeWith edges target t) cands) of+        Right (_, dn) -> Just dn+        Left _        -> Nothing++    -- dictionary variable of the class's MathValue instance, following+    -- the expander's naming scheme (lowerFirst class ++ type names)+    mvDictName :: String -> String+    mvDictName cn = case cn of+      (c:cs) -> toLower c : cs ++ "MathValue"+      []     -> "MathValue"+ evalExprShallow _ expr = throwErrorWithTrace (NotImplemented ("evalExprShallow for " ++ show expr))  evalExprDeep :: Env -> IExpr -> EvalM EgisonValue@@ -649,15 +797,22 @@ newApplyObjThunkRef :: Env -> WHNFData -> [Object] -> EvalM ObjectRef newApplyObjThunkRef env fn objs = liftIO . newIORef $ newApplyObjThunk env fn objs +-- | Helper for applyRef: check if a tensor WHNFData has shape rank exactly+-- one greater than its index count (= one DF-pending dimension). Other+-- WHNFData shapes return False so we never use a partial pattern match.+isTensorWithDFOne :: WHNFData -> Bool+isTensorWithDFOne (ITensor (Tensor s _ i)) = length s - length i == 1+isTensorWithDFOne _                        = False+ applyRef :: Env -> WHNFData -> [ObjectRef] -> EvalM WHNFData applyRef env (Value (TensorData (Tensor s1 t1 i1))) refs = do   tds <- mapM evalRef refs-  if length s1 > length i1 && all (\(ITensor (Tensor s _ i)) -> length s - length i == 1) tds+  if length s1 > length i1 && all isTensorWithDFOne tds     then do       symId <- fresh       let argnum = length tds-          subjs = map (Sub . symbolScalarData symId . show) [1 .. argnum]-          supjs = map (Sup . symbolScalarData symId . show) [1 .. argnum]+          subjs = map (Sub . symbolCASData symId . show) [1 .. argnum]+          supjs = map (Sup . symbolCASData symId . show) [1 .. argnum]       dot <- evalExprShallow env (IVarExpr ".")       tds' <- mapM toTensor tds       let args' = Value (TensorData (Tensor s1 t1 (i1 ++ supjs))) : map (ITensor . addscript) (zip subjs tds')@@ -665,12 +820,12 @@     else throwError $ Default "applyObj" applyRef env (ITensor (Tensor s1 t1 i1)) refs = do   tds <- mapM evalRef refs-  if length s1 > length i1 && all (\(ITensor (Tensor s _ i)) -> length s - length i == 1) tds+  if length s1 > length i1 && all isTensorWithDFOne tds     then do       symId <- fresh       let argnum = length tds-          subjs = map (Sub . symbolScalarData symId . show) [1 .. argnum]-          supjs = map (Sup . symbolScalarData symId . show) [1 .. argnum]+          subjs = map (Sub . symbolCASData symId . show) [1 .. argnum]+          supjs = map (Sup . symbolCASData symId . show) [1 .. argnum]       dot <- evalExprShallow env (IVarExpr ".")       tds' <- mapM toTensor tds       let args' = ITensor (Tensor s1 t1 (i1 ++ supjs)) : map (ITensor . addscript) (zip subjs tds')@@ -705,30 +860,30 @@   case args of     [Value World] -> m     arg : _       -> throwErrorWithTrace (TypeMismatch "world" arg)-applyRef _ (Value (ScalarData (SingleTerm 1 [(FunctionData sym args, 1)]))) refs = do+applyRef _ (Value (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData sym args, 1)]]))) refs = do   newArgs <- mapM (\ref -> evalRef ref >>= evalWHNF) refs-  newScalars <- mapM (\arg -> case arg of-    ScalarData s -> return s-    _ -> throwErrorWithTrace (TypeMismatch "scalar" (Value arg))) newArgs-  when (length newScalars /= length args) $+  newCASVals <- mapM (\arg -> case arg of+    CASData c -> return c+    _         -> throwErrorWithTrace (TypeMismatch "scalar" (Value arg))) newArgs+  when (length newCASVals /= length args) $     throwError (Default ("function applied to wrong number of arguments: expected "-      ++ show (length args) ++ ", got " ++ show (length newScalars)))-  return $ Value (ScalarData (SingleTerm 1 [(FunctionData sym newScalars, 1)]))-applyRef _ (Value (ScalarData fn@(SingleTerm 1 [(Symbol _ symName _, 1)]))) refs = do+      ++ show (length args) ++ ", got " ++ show (length newCASVals)))+  return $ Value $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData sym newCASVals, 1)]]+applyRef _ (Value (CASData fn@(CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol _ symName _, 1)]]))) refs = do   args <- mapM (\ref -> evalRef ref >>= evalWHNF) refs   mExprs <- mapM (\arg -> case arg of-                            ScalarData _ -> extractScalar arg-                            _            -> throwErrorWithTrace (EgisonBug $ "to use undefined function '" ++ symName ++ "', you have to use ScalarData args")) args-  return (Value (ScalarData (SingleTerm 1 [(makeApplyExpr fn mExprs, 1)])))+                            CASData c -> return c+                            _         -> throwErrorWithTrace (EgisonBug $ "to use undefined function '" ++ symName ++ "', you have to use CASData args")) args+  return $ Value $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.makeApplyExpr fn mExprs, 1)]] -- QuoteFunction pattern: ('fact 3) should create Apply1 fact 3 -- The quoted function object is stored in QuoteFunction-applyRef env (Value (ScalarData fn@(SingleTerm 1 [(QuoteFunction funcWHNF, 1)]))) refs = do+applyRef _env (Value (CASData fn@(CASPoly [CASTerm (CASInteger 1) [(CAS.QuoteFunction _funcWHNF, 1)]]))) refs = do   args <- mapM (\ref -> evalRef ref >>= evalWHNF) refs   mExprs <- mapM (\arg -> case arg of-                            ScalarData scalar -> return scalar-                            _                 -> throwErrorWithTrace (EgisonBug $ "to use quoted function, you have to use ScalarData args")) args+                            CASData c -> return c+                            _         -> throwErrorWithTrace (EgisonBug $ "to use quoted function, you have to use CASData args")) args   -- Create Apply1/Apply2/etc with the function object-  return (Value (ScalarData (SingleTerm 1 [(makeApplyExpr fn mExprs, 1)])))+  return $ Value $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.makeApplyExpr fn mExprs, 1)]] -- Type class method dispatch: look up implementation based on first argument's type -- Uses Type from Types.hs for dispatch (not String-based typeName) applyRef env (Value (ClassMethodRef clsName methName)) refs = do@@ -944,17 +1099,23 @@         _    -> return MNil     (0, MState e l s b [MAtom (IForallPat p1 p2) m t], MState{ mTrees = trees }) -> do       states <- processMStatesAllDFSForall (msingleton (MState e l (ForallPatContext [] []:s) b [MAtom p1 m t]))-      statess' <- mmap (\(MState e' l' (ForallPatContext ms ts:s') b' []) -> do-                            let mat' = makeTuple ms-                            tgt' <- makeITuple ts-                            processMStatesAllDFSForall (msingleton (MState e' l' (ForallPatContext [] []:s') b' [MAtom p2 tgt' mat']))) states+      statess' <- mmap (\ms_state -> case ms_state of+                            MState e' l' (ForallPatContext ms ts:s') b' [] -> do+                              let mat' = makeTuple ms+                              tgt' <- makeITuple ts+                              processMStatesAllDFSForall (msingleton (MState e' l' (ForallPatContext [] []:s') b' [MAtom p2 tgt' mat']))+                            _ -> error "processMState (forall): unexpected MState shape (invariant violation)")+                       states       b <- mAny (\case                    MNil -> return True                    _    -> return False) statess'       if b         then return MNil --        else return MNil-        else do nstatess <- mmap (mmap (\(MState e' l' (ForallPatContext [] []:s') b' []) -> return $ MState e' l' s' b' trees)) statess'+        else do nstatess <- mmap (mmap (\ms_state -> case ms_state of+                                            MState e' l' (ForallPatContext [] []:s') b' [] -> return $ MState e' l' s' b' trees+                                            _ -> error "processMState (forall): unexpected nstate shape (invariant violation)"))+                                  statess'                 mconcat nstatess     _ -> processMState' state  where@@ -1061,7 +1222,7 @@       startNumRef <- newEvaluatedObjectRef $ Value $ toEgison (startNum - 1)       ends'       <- evalExprShallow env' ends       case ends' of-        Value (ScalarData _) -> do -- the case when the end numbers are an integer+        Value (CASData _) -> do -- the case when the end numbers are an integer           endsRef  <- newEvaluatedObjectRef ends'           inners   <- liftIO . newIORef $ Sq.fromList [IElement endsRef]           endsRef' <- liftIO $ newIORef (WHNF (ICollection inners))@@ -1223,26 +1384,68 @@     Nothing      -> throwErrorWithTrace PrimitiveMatchFailure     Just binding -> return binding --- Helper functions to convert internal math types to ScalarData (MathExpr)-polyExprToScalarData :: PolyExpr -> ScalarData-polyExprToScalarData polyExpr = Div polyExpr (Plus [Term 1 []])+-- Helper: Extract function object from CASValue if it contains QuoteFunction+extractFunctionObjectCAS :: CASValue -> WHNFData+extractFunctionObjectCAS (CAS.CASFactor (CAS.QuoteFunction funcWHNF)) = funcWHNF+extractFunctionObjectCAS (CAS.CASPoly [CAS.CASTerm (CAS.CASInteger 1) [(CAS.QuoteFunction funcWHNF, 1)]]) = funcWHNF+extractFunctionObjectCAS cv = Value (CASData cv) -termExprToScalarData :: TermExpr -> ScalarData-termExprToScalarData termExpr = Div (Plus [termExpr]) (Plus [Term 1 []])+-- Helper: Extract numerator from CASValue+getCASNumerator :: CASValue -> CASValue+getCASNumerator (CAS.CASFrac num _) = num+getCASNumerator cv = cv -symbolExprToScalarData :: SymbolExpr -> ScalarData-symbolExprToScalarData symbolExpr = Div (Plus [Term 1 [(symbolExpr, 1)]]) (Plus [Term 1 []])+-- Helper: Extract denominator from CASValue+getCASenominator :: CASValue -> CASValue+getCASenominator (CAS.CASFrac _ den) = den+getCASenominator _ = CAS.CASInteger 1 --- Check if pattern is a pattern variable-isPatternVar :: IPrimitiveDataPattern -> Bool-isPatternVar (PDPatVar _) = True-isPatternVar _            = False+-- Helper: Convert CASValue to list of CASTerms+casToTerms :: CASValue -> [CAS.CASTerm]+casToTerms (CAS.CASPoly terms) = terms+casToTerms (CAS.CASInteger 0) = []+casToTerms (CAS.CASInteger n) = [CAS.CASTerm (CAS.CASInteger n) []]+casToTerms (CAS.CASFactor sym) = [CAS.CASTerm (CAS.CASInteger 1) [(sym, 1)]]+casToTerms cv = [CAS.CASTerm cv []] --- Helper: Extract function object from ScalarData if it contains QuoteFunction-extractFunctionObject :: ScalarData -> WHNFData-extractFunctionObject (SingleTerm 1 [(QuoteFunction funcWHNF, 1)]) = funcWHNF-extractFunctionObject scalarData = Value (ScalarData scalarData)+-- Helper: Convert CAS.SymbolExpr to CASValue (single term with coefficient 1)+symbolToCASValue :: CAS.SymbolExpr -> CASValue+symbolToCASValue sym = CAS.CASPoly [CAS.CASTerm (CAS.CASInteger 1) [(sym, 1)]] +-- Helper: Convert CASTerm to CASValue.+-- For an Integer-coefficient constant term, return the bare CASInteger so+-- downstream user code (e.g. `map`/arithmetic over the term list) sees the+-- canonical numeric form. For non-Integer constants (e.g. CASFrac+-- coefficients in level-4 `Poly (Frac Integer)` polynomials) we must wrap+-- in CASPoly to preserve the `Frac (Plus [Term n xs]) (Plus [Term 1 []])`+-- shape that `term $ $` etc. PDPs in lib/math/expression.egi expect —+-- otherwise extraction of the constant Frac coefficient via `term $a _`+-- silently fails (the bare CASFrac exposes its denom != 1).+termToCASValue :: CAS.CASTerm -> CASValue+termToCASValue (CAS.CASTerm coeff@(CAS.CASInteger _) []) = coeff+termToCASValue t = CAS.CASPoly [t]++-- Helper: Extract SymbolExpr from CASValue if it's a single-symbol single-term polynomial+-- Returns Nothing if not a simple symbol+extractSymbolExpr :: CASValue -> Maybe CAS.SymbolExpr+extractSymbolExpr (CAS.CASFactor sym) = Just sym+extractSymbolExpr (CAS.CASPoly [CAS.CASTerm (CAS.CASInteger 1) [(sym, 1)]]) = Just sym+extractSymbolExpr _ = Nothing++-- Helper: Extract coefficient and monomials from CASValue (expects single-term poly)+extractTerm :: CASValue -> Maybe (CASValue, CAS.Monomial)+extractTerm (CAS.CASFactor sym) = Just (CAS.CASInteger 1, [(sym, 1)])+extractTerm (CAS.CASPoly [CAS.CASTerm coeff mono]) = Just (coeff, mono)+extractTerm (CAS.CASInteger n) = Just (CAS.CASInteger n, [])+extractTerm _ = Nothing++-- Helper: Convert Index CASValue to a CASValue representation+-- We wrap the index expression in InductiveData for pattern matching+indexToCASValue :: Index CASValue -> CASValue+indexToCASValue (Sub cv) = cv  -- For now, just return the inner value+indexToCASValue (Sup cv) = cv+indexToCASValue (User cv) = cv+ primitiveDataPatternMatch :: IPrimitiveDataPattern -> ObjectRef -> MatchM [Binding] primitiveDataPatternMatch PDWildCard _        = return [] primitiveDataPatternMatch (PDPatVar name) ref = return [(name, ref)]@@ -1283,142 +1486,164 @@   case whnf of     Value val | val == evalConstant expr -> return []     _                                    -> matchFail--- ScalarData (MathExpr) primitive patterns-primitiveDataPatternMatch (PDDivPat patNum patDen) ref = do+-- CASValue primitive patterns+-- All patterns work directly with CASData CASValue, no intermediate types needed+primitiveDataPatternMatch (PDFracPat patNum patDen) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (ScalarData (Div num den)) -> do-      -- Pattern variable の場合は PolyExpr -> ScalarData に変換-      let numVal = if isPatternVar patNum -                   then Value (ScalarData (polyExprToScalarData num))-                   else Value (PolyExprData num)-      let denVal = if isPatternVar patDen-                   then Value (ScalarData (polyExprToScalarData den))-                   else Value (PolyExprData den)-      numRef <- lift $ newEvaluatedObjectRef numVal-      denRef <- lift $ newEvaluatedObjectRef denVal+    Value (CASData cv) -> do+      let num = getCASNumerator cv+          den = getCASenominator cv+      -- Always return CASData for both numerator and denominator+      numRef <- lift $ newEvaluatedObjectRef (Value (CASData num))+      denRef <- lift $ newEvaluatedObjectRef (Value (CASData den))       (++) <$> primitiveDataPatternMatch patNum numRef            <*> primitiveDataPatternMatch patDen denRef     _ -> matchFail primitiveDataPatternMatch (PDPlusPat patTerms) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (PolyExprData (Plus terms)) -> do-      -- Pattern variable の場合は [TermExpr] -> [ScalarData] に変換-      let termsCol = if isPatternVar patTerms-                     then Value $ Collection $ Sq.fromList $ map (ScalarData . termExprToScalarData) terms-                     else Value $ Collection $ Sq.fromList $ map TermExprData terms+    Value (CASData cv) -> do+      -- Extract terms from CASValue and convert each to CASData+      let terms = casToTerms cv+      let termsCol = Value $ Collection $ Sq.fromList $+                     map (\t -> CASData (termToCASValue t)) terms       termsRef <- lift $ newEvaluatedObjectRef termsCol       primitiveDataPatternMatch patTerms termsRef     _ -> matchFail primitiveDataPatternMatch (PDTermPat patCoeff patMonomials) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (TermExprData (Term coeff monomials)) -> do-      coeffRef <- lift $ newEvaluatedObjectRef (Value (toEgison coeff))-      -- Pattern variable の場合は [(SymbolExpr, Integer)] -> [(ScalarData, Integer)] に変換-      let monomialsCol = if isPatternVar patMonomials-                         then Value $ Collection $ Sq.fromList $ map (\(sym, exp) -> Tuple [ScalarData (symbolExprToScalarData sym), toEgison exp]) monomials-                         else Value $ Collection $ Sq.fromList $ map (\(sym, exp) -> Tuple [SymbolExprData sym, toEgison exp]) monomials-      monomialsRef <- lift $ newEvaluatedObjectRef monomialsCol-      (++) <$> primitiveDataPatternMatch patCoeff coeffRef-           <*> primitiveDataPatternMatch patMonomials monomialsRef+    Value (CASData cv) -> do+      -- Extract term from CASValue (expects single-term polynomial)+      case extractTerm cv of+        Just (coeff, monomials) -> do+          coeffRef <- lift $ newEvaluatedObjectRef (Value (CASData coeff))+          -- Convert [(SymbolExpr, Integer)] -> [(CASData, Integer)]+          let monomialsCol = Value $ Collection $ Sq.fromList $+                             map (\(sym, expo) -> Tuple [CASData (symbolToCASValue sym), toEgison expo]) monomials+          monomialsRef <- lift $ newEvaluatedObjectRef monomialsCol+          (++) <$> primitiveDataPatternMatch patCoeff coeffRef+               <*> primitiveDataPatternMatch patMonomials monomialsRef+        Nothing -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDSymbolPat patName patIndices) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (SymbolExprData (Symbol _ name indices)) -> do-      nameRef <- lift $ newEvaluatedObjectRef (Value (String (T.pack name)))-      -- [Index ScalarData]をCollectionに変換-      let indicesCol = Value $ Collection $ Sq.fromList $ map IndexExprData indices-      indicesRef <- lift $ newEvaluatedObjectRef indicesCol-      (++) <$> primitiveDataPatternMatch patName nameRef-           <*> primitiveDataPatternMatch patIndices indicesRef+    Value (CASData cv) -> do+      -- Extract symbol from CASValue+      case extractSymbolExpr cv of+        Just (CAS.Symbol _ name indices) -> do+          nameRef <- lift $ newEvaluatedObjectRef (Value (String (T.pack name)))+          -- [Index CASValue] -> Collection of CASData (wrapped indices)+          let indicesCol = Value $ Collection $ Sq.fromList $ map (CASData . indexToCASValue) indices+          indicesRef <- lift $ newEvaluatedObjectRef indicesCol+          (++) <$> primitiveDataPatternMatch patName nameRef+               <*> primitiveDataPatternMatch patIndices indicesRef+        _ -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDApply1Pat patFn patArg) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (SymbolExprData (Apply1 fn arg)) -> do-      fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObject fn)-      argRef <- lift $ newEvaluatedObjectRef (Value (ScalarData arg))-      (++) <$> primitiveDataPatternMatch patFn fnRef-           <*> primitiveDataPatternMatch patArg argRef+    Value (CASData cv) -> do+      case extractSymbolExpr cv of+        Just (CAS.Apply1 fn arg) -> do+          fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObjectCAS fn)+          argRef <- lift $ newEvaluatedObjectRef (Value (CASData arg))+          (++) <$> primitiveDataPatternMatch patFn fnRef+               <*> primitiveDataPatternMatch patArg argRef+        _ -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDApply2Pat patFn patArg1 patArg2) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (SymbolExprData (Apply2 fn arg1 arg2)) -> do-      fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObject fn)-      arg1Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg1))-      arg2Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg2))-      (++) <$> primitiveDataPatternMatch patFn fnRef-           <*> ((++) <$> primitiveDataPatternMatch patArg1 arg1Ref-                     <*> primitiveDataPatternMatch patArg2 arg2Ref)+    Value (CASData cv) -> do+      case extractSymbolExpr cv of+        Just (CAS.Apply2 fn arg1 arg2) -> do+          fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObjectCAS fn)+          arg1Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg1))+          arg2Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg2))+          (++) <$> primitiveDataPatternMatch patFn fnRef+               <*> ((++) <$> primitiveDataPatternMatch patArg1 arg1Ref+                         <*> primitiveDataPatternMatch patArg2 arg2Ref)+        _ -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDApply3Pat patFn patArg1 patArg2 patArg3) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (SymbolExprData (Apply3 fn arg1 arg2 arg3)) -> do-      fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObject fn)-      arg1Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg1))-      arg2Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg2))-      arg3Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg3))-      (++) <$> primitiveDataPatternMatch patFn fnRef-           <*> ((++) <$> primitiveDataPatternMatch patArg1 arg1Ref-                     <*> ((++) <$> primitiveDataPatternMatch patArg2 arg2Ref-                               <*> primitiveDataPatternMatch patArg3 arg3Ref))+    Value (CASData cv) -> do+      case extractSymbolExpr cv of+        Just (CAS.Apply3 fn arg1 arg2 arg3) -> do+          fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObjectCAS fn)+          arg1Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg1))+          arg2Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg2))+          arg3Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg3))+          (++) <$> primitiveDataPatternMatch patFn fnRef+               <*> ((++) <$> primitiveDataPatternMatch patArg1 arg1Ref+                         <*> ((++) <$> primitiveDataPatternMatch patArg2 arg2Ref+                                   <*> primitiveDataPatternMatch patArg3 arg3Ref))+        _ -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDApply4Pat patFn patArg1 patArg2 patArg3 patArg4) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (SymbolExprData (Apply4 fn arg1 arg2 arg3 arg4)) -> do-      fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObject fn)-      arg1Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg1))-      arg2Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg2))-      arg3Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg3))-      arg4Ref <- lift $ newEvaluatedObjectRef (Value (ScalarData arg4))-      (++) <$> primitiveDataPatternMatch patFn fnRef-           <*> ((++) <$> primitiveDataPatternMatch patArg1 arg1Ref-                     <*> ((++) <$> primitiveDataPatternMatch patArg2 arg2Ref-                               <*> ((++) <$> primitiveDataPatternMatch patArg3 arg3Ref-                                         <*> primitiveDataPatternMatch patArg4 arg4Ref)))+    Value (CASData cv) -> do+      case extractSymbolExpr cv of+        Just (CAS.Apply4 fn arg1 arg2 arg3 arg4) -> do+          fnRef <- lift $ newEvaluatedObjectRef (extractFunctionObjectCAS fn)+          arg1Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg1))+          arg2Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg2))+          arg3Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg3))+          arg4Ref <- lift $ newEvaluatedObjectRef (Value (CASData arg4))+          (++) <$> primitiveDataPatternMatch patFn fnRef+               <*> ((++) <$> primitiveDataPatternMatch patArg1 arg1Ref+                         <*> ((++) <$> primitiveDataPatternMatch patArg2 arg2Ref+                                   <*> ((++) <$> primitiveDataPatternMatch patArg3 arg3Ref+                                             <*> primitiveDataPatternMatch patArg4 arg4Ref)))+        _ -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDQuotePat patExpr) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (SymbolExprData (Quote expr)) -> do-      exprRef <- lift $ newEvaluatedObjectRef (Value (ScalarData expr))-      primitiveDataPatternMatch patExpr exprRef+    Value (CASData cv) -> do+      case extractSymbolExpr cv of+        Just (CAS.Quote expr) -> do+          exprRef <- lift $ newEvaluatedObjectRef (Value (CASData expr))+          primitiveDataPatternMatch patExpr exprRef+        _ -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDFunctionPat patName patArgs) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (SymbolExprData (FunctionData name args)) -> do-      nameRef <- lift $ newEvaluatedObjectRef (Value (ScalarData name))-      let argsCol = Value $ Collection $ Sq.fromList $ map ScalarData args-      argsRef <- lift $ newEvaluatedObjectRef argsCol-      (++) <$> primitiveDataPatternMatch patName nameRef-           <*> primitiveDataPatternMatch patArgs argsRef+    Value (CASData cv) -> do+      case extractSymbolExpr cv of+        Just (CAS.FunctionData name args) -> do+          nameRef <- lift $ newEvaluatedObjectRef (Value (CASData name))+          let argsCol = Value $ Collection $ Sq.fromList $ map CASData args+          argsRef <- lift $ newEvaluatedObjectRef argsCol+          (++) <$> primitiveDataPatternMatch patName nameRef+               <*> primitiveDataPatternMatch patArgs argsRef+        _ -> matchFail     _ -> matchFail primitiveDataPatternMatch (PDSubPat patExpr) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (IndexExprData (Sub expr)) -> do-      exprRef <- lift $ newEvaluatedObjectRef (Value (ScalarData expr))+    Value (CASIndexData (Sub expr)) -> do+      exprRef <- lift $ newEvaluatedObjectRef (Value (CASData expr))       primitiveDataPatternMatch patExpr exprRef     _ -> matchFail primitiveDataPatternMatch (PDSupPat patExpr) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (IndexExprData (Sup expr)) -> do-      exprRef <- lift $ newEvaluatedObjectRef (Value (ScalarData expr))+    Value (CASIndexData (Sup expr)) -> do+      exprRef <- lift $ newEvaluatedObjectRef (Value (CASData expr))       primitiveDataPatternMatch patExpr exprRef     _ -> matchFail primitiveDataPatternMatch (PDUserPat patExpr) ref = do   whnf <- lift $ evalRef ref   case whnf of-    Value (IndexExprData (User expr)) -> do-      exprRef <- lift $ newEvaluatedObjectRef (Value (ScalarData expr))+    Value (CASIndexData (User expr)) -> do+      exprRef <- lift $ newEvaluatedObjectRef (Value (CASData expr))       primitiveDataPatternMatch patExpr exprRef     _ -> matchFail @@ -1447,8 +1672,11 @@   xs' <- mapM newEvaluatedObjectRef xs   return $ ITensor (Tensor s (V.fromList xs') []) -newITensor :: Shape -> [ObjectRef] -> WHNFData-newITensor s refs = ITensor (Tensor s (V.fromList refs) [])+-- A rank-zero tensor is its sole scalar component.  Do not use the component+-- count alone here: shapes [0] and [1] are still rank-one tensors.+newITensor :: Shape -> [ObjectRef] -> EvalM WHNFData+newITensor [] [ref] = evalRef ref+newITensor s refs = return $ ITensor (Tensor s (V.fromList refs) [])  -- Refer the specified tensor index with potential overriding of the index. refTensorWithOverride :: TensorComponent a b => Bool -> [Index EgisonValue] -> Tensor b -> EvalM a@@ -1472,4 +1700,3 @@  makeBindings' :: [String] -> [ObjectRef] -> [Binding] makeBindings' xs = zip (map stringToVar xs)-
hs-src/Language/Egison/Data.hs view
@@ -20,14 +20,17 @@     , EgisonData (..)     , Tensor (..)     , Shape-    -- * Scalar-    , symbolScalarData-    , symbolScalarData'+    -- * Symbol helpers     , getSymId-    , getSymName-    , mathExprToEgison-    , egisonToScalarData-    , extractScalar+    -- * CAS types and helpers+    , CASValue(..)+    , CASTerm(..)+    , extractCASValue+    , symbolCASData+    , quoteCASData+    , quoteFunctionCASData+    , functionCASData+    , applyCASData     -- * Internal data     , Object (..)     , ObjectRef@@ -75,7 +78,7 @@ import qualified Data.Vector                      as V  import           Data.List                        (intercalate, sortOn)-import           Data.Text                        (Text, pack, unpack)+import           Data.Text                        (Text) import           Text.Show.Unicode                (ushow)  import           Data.Ratio@@ -85,6 +88,7 @@ import           Language.Egison.EvalState import           Language.Egison.IExpr import           Language.Egison.Math+import qualified Language.Egison.Math.CAS as CAS import           Language.Egison.RState  --@@ -96,7 +100,7 @@   | Char Char   | String Text   | Bool Bool-  | ScalarData ScalarData+  | CASData CASValue  -- Computer algebra system type (CAS)   | TensorData (Tensor EgisonValue)   | Float Double   | InductiveData String [EgisonValue]@@ -121,11 +125,8 @@   -- ClassMethodRef className methodName   -- Looks up implementation from the instance environment in EvalState   | ClassMethodRef String String-  -- MathExpr internal types for direct pattern matching-  | PolyExprData PolyExpr-  | TermExprData TermExpr-  | SymbolExprData SymbolExpr-  | IndexExprData (Index ScalarData)+  -- CAS internal type for index pattern matching (subscript, superscript, user)+  | CASIndexData (Index CASValue)  type Matcher = EgisonValue @@ -149,161 +150,39 @@ type Shape = [Integer]  ----- Scalars+-- CAS Value Helpers -- -symbolScalarData :: String -> String -> EgisonValue-symbolScalarData id name = ScalarData (SingleTerm 1 [(Symbol id name [], 1)])--symbolScalarData' :: String -> ScalarData-symbolScalarData' name = SingleTerm 1 [(Symbol "" name [], 1)]-+-- | Extract symbol ID from a CAS symbol value getSymId :: EgisonValue -> String-getSymId (ScalarData (SingleTerm 1 [(Symbol id _ _, _)])) = id--getSymName :: EgisonValue -> String-getSymName (ScalarData (SingleTerm 1 [(Symbol _ name [], 1)])) = name--mathExprToEgison :: ScalarData -> EgisonValue-mathExprToEgison (Div p1 p2) = InductiveData "Div" [polyExprToEgison p1, polyExprToEgison p2]--polyExprToEgison :: PolyExpr -> EgisonValue-polyExprToEgison (Plus ts) = InductiveData "Plus" [Collection (Sq.fromList (map termExprToEgison ts))]--termExprToEgison :: TermExpr -> EgisonValue-termExprToEgison (Term a xs) = InductiveData "Term" [toEgison a, Collection (Sq.fromList (map symbolExprToEgison xs))]--symbolExprToEgison :: (SymbolExpr, Integer) -> EgisonValue-symbolExprToEgison (Symbol id x js, n) = Tuple [InductiveData "Symbol" [symbolScalarData id x, f js], toEgison n]- where-  f js = Collection (Sq.fromList (map scalarIndexToEgison js))-symbolExprToEgison (Apply1 fn a1, n) = Tuple [InductiveData "Apply1" [ScalarData fn, ScalarData a1], toEgison n]-symbolExprToEgison (Apply2 fn a1 a2, n) = Tuple [InductiveData "Apply2" [ScalarData fn, ScalarData a1, ScalarData a2], toEgison n]-symbolExprToEgison (Apply3 fn a1 a2 a3, n) = Tuple [InductiveData "Apply3" [ScalarData fn, ScalarData a1, ScalarData a2, ScalarData a3], toEgison n]-symbolExprToEgison (Apply4 fn a1 a2 a3 a4, n) = Tuple [InductiveData "Apply4" [ScalarData fn, ScalarData a1, ScalarData a2, ScalarData a3, ScalarData a4], toEgison n]-symbolExprToEgison (Quote mExpr, n) = Tuple [InductiveData "Quote" [mathExprToEgison mExpr], toEgison n]-symbolExprToEgison (QuoteFunction (Value funcVal), n) = Tuple [InductiveData "QuoteFunction" [funcVal], toEgison n]-symbolExprToEgison (QuoteFunction whnf, n) = error $ "symbolExprToEgison: QuoteFunction with non-Value WHNF: " ++ show whnf-symbolExprToEgison (FunctionData name args, n) =-  Tuple [InductiveData "Function" [ScalarData name, Collection (Sq.fromList (map ScalarData args))], toEgison n]--scalarIndexToEgison :: Index ScalarData -> EgisonValue-scalarIndexToEgison (Sup k)  = InductiveData "Sup"  [ScalarData k]-scalarIndexToEgison (Sub k)  = InductiveData "Sub"  [ScalarData k]-scalarIndexToEgison (User k) = InductiveData "User" [ScalarData k]---- Direct index conversion for primitive pattern matching-indexToEgison :: Index ScalarData -> EgisonValue-indexToEgison = IndexExprData---- Implementation of 'toMathExpr' (Primitive function)-egisonToScalarData :: EgisonValue -> EvalM ScalarData-egisonToScalarData (InductiveData "Div" [p1, p2]) = Div <$> egisonToPolyExpr p1 <*> egisonToPolyExpr p2-egisonToScalarData p1@(InductiveData "Plus" _) = Div <$> egisonToPolyExpr p1 <*> return (Plus [Term 1 []])-egisonToScalarData t1@(InductiveData "Term" _) = do-  t1' <- egisonToTermExpr t1-  return $ Div (Plus [t1']) (Plus [Term 1 []])-egisonToScalarData s1@(InductiveData "Symbol" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 ::Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData s1@(InductiveData "Apply1" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 :: Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData s1@(InductiveData "Apply2" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 :: Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData s1@(InductiveData "Apply3" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 :: Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData s1@(InductiveData "Apply4" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 :: Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData s1@(InductiveData "Quote" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 :: Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData s1@(InductiveData "QuoteFunction" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 :: Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData s1@(InductiveData "Function" _) = do-  s1' <- egisonToSymbolExpr (Tuple [s1, toEgison (1 :: Integer)])-  return $ SingleTerm 1 [s1']-egisonToScalarData (ScalarData s) = return s-egisonToScalarData val = throwErrorWithTrace (TypeMismatch "math expression" (Value val))--egisonToPolyExpr :: EgisonValue -> EvalM PolyExpr-egisonToPolyExpr (InductiveData "Plus" [Collection ts]) = Plus <$> mapM egisonToTermExpr (toList ts)-egisonToPolyExpr val                                    = throwErrorWithTrace (TypeMismatch "math poly expression" (Value val))+getSymId val = case val of+  CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId _ _, _)]]) -> symId+  _ -> error "getSymId: not a symbol" -egisonToTermExpr :: EgisonValue -> EvalM TermExpr-egisonToTermExpr (InductiveData "Term" [n, Collection ts]) = Term <$> fromEgison n <*> mapM egisonToSymbolExpr (toList ts)-egisonToTermExpr val                                       = throwErrorWithTrace (TypeMismatch "math term expression" (Value val))+-- | Create a symbol CASValue+symbolCASData :: String -> String -> EgisonValue+symbolCASData symId name = CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name [], 1)]] -egisonToSymbolExpr :: EgisonValue -> EvalM (SymbolExpr, Integer)-egisonToSymbolExpr (Tuple [InductiveData "Symbol" [x, Collection seq], n]) = do-  let js = toList seq-  js' <- mapM egisonToScalarIndex js-  n' <- fromEgison n-  case x of-    (ScalarData (Div (Plus [Term 1 [(Symbol id name [], 1)]]) (Plus [Term 1 []]))) ->-      return (Symbol id name js', n')-egisonToSymbolExpr (Tuple [InductiveData "Apply1" [fn, a1], n]) = do-  fn' <- extractScalar fn-  a1' <- egisonToScalarData a1-  n' <- fromEgison n-  return (Apply1 fn' a1', n')-egisonToSymbolExpr (Tuple [InductiveData "Apply2" [fn, a1, a2], n]) = do-  fn' <- extractScalar fn-  a1' <- egisonToScalarData a1-  a2' <- egisonToScalarData a2-  n' <- fromEgison n-  return (Apply2 fn' a1' a2', n')-egisonToSymbolExpr (Tuple [InductiveData "Apply3" [fn, a1, a2, a3], n]) = do-  fn' <- extractScalar fn-  a1' <- egisonToScalarData a1-  a2' <- egisonToScalarData a2-  a3' <- egisonToScalarData a3-  n' <- fromEgison n-  return (Apply3 fn' a1' a2' a3', n')-egisonToSymbolExpr (Tuple [InductiveData "Apply4" [fn, a1, a2, a3, a4], n]) = do-  fn' <- extractScalar fn-  a1' <- egisonToScalarData a1-  a2' <- egisonToScalarData a2-  a3' <- egisonToScalarData a3-  a4' <- egisonToScalarData a4-  n' <- fromEgison n-  return (Apply4 fn' a1' a2' a3' a4', n')-egisonToSymbolExpr (Tuple [InductiveData "Quote" [mExpr], n]) = do-  mExpr' <- egisonToScalarData mExpr-  n' <- fromEgison n-  return (Quote mExpr', n')-egisonToSymbolExpr (Tuple [InductiveData "QuoteFunction" [funcVal], n]) = do-  n' <- fromEgison n-  return (QuoteFunction (Value funcVal), n')-egisonToSymbolExpr (Tuple [InductiveData "Function" [name, Collection args], n]) = do-  name' <- extractScalar name-  args' <- mapM extractScalar (toList args)-  n' <- fromEgison n-  return (FunctionData name' args', n')-egisonToSymbolExpr val = throwErrorWithTrace (TypeMismatch "math symbol expression" (Value val))+-- | Create a Quote CASValue+quoteCASData :: CASValue -> EgisonValue+quoteCASData cv = CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Quote cv, 1)]] -egisonToScalarIndex :: EgisonValue -> EvalM (Index ScalarData)-egisonToScalarIndex j = case j of-  InductiveData "Sup"  [ScalarData k] -> return (Sup k)-  InductiveData "Sub"  [ScalarData k] -> return (Sub k)-  InductiveData "User" [ScalarData k] -> return (User k)-  _                                   -> throwErrorWithTrace (TypeMismatch "math symbol expression" (Value j))+-- | Create a QuoteFunction CASValue+quoteFunctionCASData :: WHNFData -> EgisonValue+quoteFunctionCASData whnf = CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.QuoteFunction whnf, 1)]] ------ ExtractScalar---+-- | Create a FunctionData CASValue+functionCASData :: CASValue -> [CASValue] -> EgisonValue+functionCASData sym args = CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData sym args, 1)]] -extractScalar :: EgisonValue -> EvalM ScalarData-extractScalar (ScalarData mExpr) = return mExpr-extractScalar val                = throwErrorWithTrace (TypeMismatch "math expression" (Value val))+-- | Create an Apply CASValue (Apply1-4 based on argument count)+applyCASData :: CASValue -> [CASValue] -> EgisonValue+applyCASData fn args = CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.makeApplyExpr fn args, 1)]] -extractString :: EgisonValue -> EvalM String-extractString (String t) = return (unpack t)-extractString val        = throwErrorWithTrace (TypeMismatch "string" (Value val))+-- | Extract CASValue from EgisonValue+extractCASValue :: EgisonValue -> EvalM CASValue+extractCASValue (CASData cv) = return cv+extractCASValue val          = throwErrorWithTrace (TypeMismatch "math expression" (Value val))  -- New-syntax version of EgisonValue pretty printer. -- TODO(momohatt): Don't make it a show instance of EgisonValue.@@ -312,7 +191,7 @@   show (String str) = ushow str   show (Bool True) = "True"   show (Bool False) = "False"-  show (ScalarData mExpr) = show mExpr+  show (CASData cv) = prettyCAS cv   show (TensorData (Tensor [_] xs js)) = "[| " ++ intercalate ", " (map show (V.toList xs)) ++ " |]" ++ concatMap show js   show (TensorData (Tensor [0, 0] _ js)) = "[| [|  |] |]" ++ concatMap show js   show (TensorData (Tensor [_, j] xs js)) = "[| " ++ intercalate ", " (f (fromIntegral j) (V.toList xs)) ++ " |]" ++ concatMap show js@@ -346,28 +225,23 @@   show Undefined = "undefined"   show World = "#<world>"   show (ClassMethodRef clsName methName) = "#<class-method " ++ clsName ++ "." ++ methName ++ ">"-  -- MathExpr internal types-  show (PolyExprData polyExpr) = show polyExpr-  show (TermExprData termExpr) = show termExpr-  show (SymbolExprData symbolExpr) = show symbolExpr-  show (IndexExprData indexExpr) = show indexExpr+  -- CAS internal type for index pattern matching+  show (CASIndexData idx) = show idx  -- False if we have to put parenthesis around it to make it an atomic expression. isAtomic :: EgisonValue -> Bool isAtomic (InductiveData _ []) = True isAtomic (InductiveData _ _)  = False-isAtomic (ScalarData m)       = isAtom m-isAtomic (PolyExprData _)     = False-isAtomic (TermExprData _)     = False-isAtomic (SymbolExprData _)   = False-isAtomic (IndexExprData _)    = False+isAtomic (CASData cv)         = casIsAtom cv+-- CAS internal type for index pattern matching+isAtomic (CASIndexData _)     = False isAtomic _                    = True  instance Eq EgisonValue where   (Char c) == (Char c')                                            = c == c'   (String str) == (String str')                                    = str == str'   (Bool b) == (Bool b')                                            = b == b'-  (ScalarData x) == (ScalarData y)                                 = x == y+  (CASData x) == (CASData y)                                       = CAS.casNormalize x == CAS.casNormalize y  -- Normalize before comparing   (TensorData (Tensor js xs _)) == (TensorData (Tensor js' xs' _)) = js == js' && xs == xs'   (Float x) == (Float x')                                          = x == x'   (InductiveData name vals) == (InductiveData name' vals')         = name == name' && vals == vals'@@ -376,11 +250,8 @@   (IntHash vals) == (IntHash vals')                                = vals == vals'   (CharHash vals) == (CharHash vals')                              = vals == vals'   (StrHash vals) == (StrHash vals')                                = vals == vals'-  -- MathExpr internal types-  (PolyExprData p) == (PolyExprData p')                            = p == p'-  (TermExprData t) == (TermExprData t')                            = t == t'-  (SymbolExprData s) == (SymbolExprData s')                        = s == s'-  (IndexExprData i) == (IndexExprData i')                          = i == i'+  -- CAS internal types+  (CASIndexData i) == (CASIndexData i')                            = i == i'   -- Temporary: searching a better solution   (Func (Just name1) _ _ _) == (Func (Just name2) _ _ _)           = name1 == name2   _ == _                                                           = False@@ -408,17 +279,35 @@   fromEgison val      = throwErrorWithTrace (TypeMismatch "bool" (Value val))  instance EgisonData Integer where-  toEgison 0 = ScalarData (Div (Plus []) (Plus [Term 1 []]))-  toEgison i = ScalarData (SingleTerm i [])-  fromEgison (ScalarData (Div (Plus []) (Plus [Term 1 []]))) = return 0-  fromEgison (ScalarData (SingleTerm x []))                  = return x-  fromEgison val                                             = throwErrorWithTrace (TypeMismatch "integer" (Value val))+  toEgison 0 = CASData (CASInteger 0)+  toEgison i = CASData (CASInteger i)+  fromEgison val = case val of+    CASData cv -> case extractCASInteger cv of+      Just n  -> return n+      Nothing -> throwErrorWithTrace (TypeMismatch "integer" (Value val))+    _ -> throwErrorWithTrace (TypeMismatch "integer" (Value val)) +-- | Extract an Integer from a CASValue, handling divisions that simplify to integers+extractCASInteger :: CASValue -> Maybe Integer+extractCASInteger cv = case cv of+  CASInteger n -> Just n+  CASPoly [] -> Just 0+  CASPoly [CASTerm coef []] -> extractCASInteger coef+  CASFrac num den -> do+    n <- extractCASInteger num+    d <- extractCASInteger den+    if d /= 0 && n `mod` d == 0 then Just (n `div` d) else Nothing+  _ -> Nothing+ instance EgisonData Rational where-  toEgison r = ScalarData $ mathNormalize' (Div (Plus [Term (numerator r) []]) (Plus [Term (denominator r) []]))-  fromEgison (ScalarData (Div (Plus []) _))                           = return 0-  fromEgison (ScalarData (Div (Plus [Term x []]) (Plus [Term y []]))) = return (x % y)-  fromEgison val                                                      = throwErrorWithTrace (TypeMismatch "rational" (Value val))+  toEgison r = CASData $ CAS.casNormalize (CAS.CASFrac (CAS.CASInteger (numerator r)) (CAS.CASInteger (denominator r)))+  fromEgison val = case val of+    CASData (CASInteger 0)                  -> return 0+    CASData (CASPoly [])                    -> return 0+    CASData (CASInteger x)                  -> return (x % 1)+    CASData (CASPoly [CASTerm (CASInteger x) []]) -> return (x % 1)+    CASData (CASFrac (CASPoly [CASTerm (CASInteger x) []]) (CASPoly [CASTerm (CASInteger y) []])) -> return (x % y)+    _                                       -> throwErrorWithTrace (TypeMismatch "rational" (Value val))  instance EgisonData Double where   toEgison f = Float f@@ -533,21 +422,21 @@  -- | Environment: list of layers (for scoping) plus optional index context, -- plus a separate store for pattern functions.-data Env = Env [EnvLayer] (Maybe (String, [Index (Maybe ScalarData)])) PatFuncEnv+data Env = Env [EnvLayer] (Maybe (String, [Index (Maybe CASValue)])) PatFuncEnv  type Binding = (Var, ObjectRef)  instance {-# OVERLAPPING #-} Show (Index EgisonValue) where   show (Sup i) = case i of-    ScalarData (SingleTerm 1 [(Symbol _ _ (_:_), 1)]) -> "~[" ++ show i ++ "]"-    _                                                 -> "~" ++ show i+    CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol _ _ (_:_), 1)]]) -> "~[" ++ show i ++ "]"+    _ -> "~" ++ show i   show (Sub i) = case i of-    ScalarData (SingleTerm 1 [(Symbol _ _ (_:_), 1)]) -> "_[" ++ show i ++ "]"-    _                                                 -> "_" ++ show i+    CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol _ _ (_:_), 1)]]) -> "_[" ++ show i ++ "]"+    _ -> "_" ++ show i   show (SupSub i) = "~_" ++ show i   show (User i) = case i of-    ScalarData (SingleTerm 1 [(Symbol _ _ (_:_), 1)]) -> "_[" ++ show i ++ "]"-    _                                                 -> "|" ++ show i+    CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol _ _ (_:_), 1)]]) -> "_[" ++ show i ++ "]"+    _ -> "|" ++ show i   show (DF i j) = "_df-" ++ show i ++ "-" ++ show j  nullEnv :: Env
hs-src/Language/Egison/Data/Utils.hs view
@@ -95,13 +95,13 @@ showIndexValues :: [Index EgisonValue] -> [String] showIndexValues = map showIndexValue   where-    showIndexValue (Sub val) = "_<val>"-    showIndexValue (Sup val) = "~<val>"-    showIndexValue (SupSub val) = "~_<val>"-    showIndexValue (User val) = "@<val>"-    showIndexValue (DF id n) = "_df-" ++ show id ++ "-" ++ show n-    showIndexValue (MultiSub val s _) = "_(..." ++ show s ++ "...)"-    showIndexValue (MultiSup val s _) = "~(..." ++ show s ++ "...)"+    showIndexValue (Sub _val) = "_<val>"+    showIndexValue (Sup _val) = "~<val>"+    showIndexValue (SupSub _val) = "~_<val>"+    showIndexValue (User _val) = "@<val>"+    showIndexValue (DF idn n) = "_df-" ++ show idn ++ "-" ++ show n+    showIndexValue (MultiSub _val s _) = "_(..." ++ show s ++ "...)"+    showIndexValue (MultiSup _val s _) = "~(..." ++ show s ++ "...)"  pmIndices :: [Index (Maybe Var)] -> [Index EgisonValue] -> EvalM [Binding] pmIndices [] [] = return []@@ -110,25 +110,33 @@   let l = fromIntegral (length vs1)   eRef <- newEvaluatedObjectRef (Value (toEgison l))   let hash = (IIntHash HL.empty)-  hash <- foldM (\hash (i, v) -> updateHash [i] v hash) hash (zip [s..(s + l - 1)] (map (\(Sub v) -> Value v) vs1)) +  hash <- foldM (\hash (i, v) -> updateHash [i] v hash) hash+                (zip [s..(s + l - 1)]+                     (map subValue vs1))   aRef <- newEvaluatedObjectRef hash   bs <- pmIndices xs vs2   return ((a, aRef) : (e, eRef) : bs)  where   isSub (Sub _) = True   isSub _       = False+  subValue (Sub v) = Value v+  subValue idx     = error ("pmIndices: expected Sub index, got: " ++ show idx) pmIndices (MultiSup (Just a) s (Just e):xs) vs = do   let (vs1, vs2) = span isSup vs   let l = fromIntegral (length vs1)   eRef <- newEvaluatedObjectRef (Value (toEgison l))   let hash = (IIntHash HL.empty)-  hash <- foldM (\hash (i, v) -> updateHash [i] v hash) hash (zip [s..(s + l - 1)] (map (\(Sup v) -> Value v) vs1)) +  hash <- foldM (\hash (i, v) -> updateHash [i] v hash) hash+                (zip [s..(s + l - 1)]+                     (map supValue vs1))   aRef <- newEvaluatedObjectRef hash   bs <- pmIndices xs vs2   return ((a, aRef) : (e, eRef) : bs)  where   isSup (Sup _) = True   isSup _       = False+  supValue (Sup v) = Value v+  supValue idx     = error ("pmIndices: expected Sup index, got: " ++ show idx)  pmIndices (x:xs) (v:vs) = do   bs <- pmIndex x v@@ -143,6 +151,17 @@ pmIndex (Sup (Just var)) (Sup val) = do   ref <- newEvaluatedObjectRef (Value val)   return [(var, ref)]+-- `def f_i_j := ...` desugars (Desugar.hs `desugarDefineWithIndices`) so+-- the function's stored Var indices are *kind-only* (`Sub Nothing` /+-- `Sup Nothing`) — the actual index names are bound separately via+-- `withSymbols` + `transpose` inside the body. When we apply `f_a_b`,+-- the access values come in here and just need to match the index kind;+-- there is no name to bind. Without these clauses every tensor function+-- defined with explicit index names (e.g. the WCS invariant computation+-- in `sample/math/geometry/thurston.egi`) raises+-- "Inconsistent tensor index: [_?] vs [_<val>]".+pmIndex (Sub Nothing) (Sub _) = return []+pmIndex (Sup Nothing) (Sup _) = return [] pmIndex expected actual = throwErrorWithTrace $ InconsistentTensorIndex (showIndexPattern [expected]) (showIndexValues [actual])  updateHash :: [Integer] -> WHNFData -> WHNFData -> EvalM WHNFData
hs-src/Language/Egison/Desugar.hs view
@@ -15,7 +15,7 @@    Design Note (design/implementation.md): Pattern matching itself is NOT desugared here. Match expressions (IMatchExpr, -IMatchAllExpr) are kept as-is and processed during evaluation (Phase 10).+IMatchAllExpr) are kept as-is and processed during evaluation (Phase 9). This allows Egison's sophisticated pattern matching to be implemented in the evaluator. -} @@ -29,15 +29,19 @@ import           Control.Monad.Except   (throwError) import           Data.Char              (toUpper) import           Data.Foldable          (foldrM)-import           Data.List              (union)+import           Data.List              (nubBy, union) import           Data.Text              (pack)  import           Language.Egison.AST import           Language.Egison.Data import           Language.Egison.IExpr import           Language.Egison.RState-import           Language.Egison.Type.Types (sanitizeMethodName, typeToName, typeConstructorName, -                                             typeExprToType, capitalizeFirst, lowerFirst, TyVar(..))+import           Language.Egison.EvalState  (MonadEval(..))+import           Language.Egison.Type.Env   (lookupClass, ClassInfo(..))+import           Language.Egison.Type.Types (sanitizeMethodName, typeToName,+                                             typeExprToType, expandTypeAliases,+                                             capitalizeFirst, lowerFirst, TyVar(..),+                                             Type(TInt, TMathValue))   desugarTopExpr :: TopExpr -> EvalM (Maybe ITopExpr)@@ -49,17 +53,22 @@   -- Type information is used for type checking, but the runtime representation is the same   -- Note: Constraints are preserved in the type scheme (by EnvBuilder),   -- and dictionary passing is handled in TypeClassExpand phase+  --+  -- (Auto-coerce elaboration was tried but reverted: wrapping `e` in+  -- `coerce` breaks inner typeclass dispatch when `e` is itself a method+  -- call needing return-type context — e.g. `def f : Frac Integer := embed 5`+  -- becomes `coerce (embed 5)`, leaving `embed`'s return type ambiguous.+  -- After tower fix + Term widening, runtime forms are already canonical+  -- in most cases, so explicit `coerce` is rarely needed.)   let name = typedVarName typedVwi       indices = typedVarIndices typedVwi       params = typedVarParams typedVwi       vwi = VarWithIndices name indices-  -- If there are typed parameters, wrap the body in a lambda   case params of     [] -> do       (var, iexpr) <- desugarDefineWithIndices vwi expr       return . Just $ IDefine var iexpr     _  -> do-      -- Create lambda arguments from typed parameters       let argPatterns = map typedParamToArgPattern params           lambdaExpr = LambdaExpr argPatterns expr       (var, iexpr) <- desugarDefineWithIndices vwi lambdaExpr@@ -135,17 +144,23 @@   if null instTypes     then return Nothing     else do-      -- Use type constructor name only (without type parameters)-      -- e.g., "Collection" not "Collectiona" for [a]-      let instTypeName = typeConstructorName (typeExprToType (head instTypes))+      -- Multi-param-friendly: concatenate type names of ALL instance types so+      -- two instances of the same class with different type-tuples get+      -- distinct dictionary names. Use `typeToName` (full type, including+      -- inner parameters) rather than `typeConstructorName` (outer only) so+      -- that e.g. `Coerce (Frac Integer) Integer` and+      -- `Coerce (Frac (Poly Integer [..])) Integer` get distinct names.+      -- cas-type aliases are expanded first so the generated names agree+      -- with EnvBuilder's registerInstanceMethods (Phase alpha).+      aliasEnv <- getCasTypeAliasEnv+      let instTypeNames = map (typeToName . expandTypeAliases aliasEnv . typeExprToType) instTypes+          instTypeName  = concat instTypeNames       -- Generate individual method definitions with constraint parameters       methodDefs <- mapM (desugarInstanceMethod constraints classNm instTypeName) methods-      -- Generate dictionary definition (with constraints if any)-      let dictDef = makeDictDef constraints classNm instTypeName methods-      -- Return all definitions-      case methodDefs of-        []  -> return Nothing-        _   -> return $ Just $ IDefineMany (dictDef : methodDefs)+      -- Generate dictionary definition with superclass references+      dictDef <- makeDictDef classNm instTypeName methods+      -- Always generate the dictionary (even for marker classes with no methods)+      return $ Just $ IDefineMany (dictDef : methodDefs)   where     desugarInstanceMethod :: [ConstraintExpr] -> String -> String -> InstanceMethod -> EvalM (Var, IExpr)     desugarInstanceMethod _constrs clsNm typNm (InstanceMethod methName params body) = do@@ -165,45 +180,59 @@       iexpr <- desugar lambdaExpr       return (var, iexpr)     -    makeDictDef :: [ConstraintExpr] -> String -> String -> [InstanceMethod] -> (Var, IExpr)-    makeDictDef _constrs clsNm typNm meths =+    makeDictDef :: String -> String -> [InstanceMethod] -> EvalM (Var, IExpr)+    makeDictDef clsNm typNm meths = do       let dictName = lowerFirst clsNm ++ typNm  -- e.g., "eqCollection"           dictVar = stringToVar dictName-          -          -- For nested instances (with constraints), the dictionary becomes a function-          -- that takes dictionary parameters and returns a hash.-          -- e.g., for instance {Eq a} Eq [a]:-          --   eqCollection = \dict_Eq -> {| ("eq", eqCollectionEq dict_Eq), ... |}-          ---          -- Dictionary parameters will be automatically added by addDictionaryParametersT-          -- after type inference, so we don't add them here manually.-          -- We just create the hash with references to the methods.-          -          hashEntries = map (makeHashEntry clsNm typNm) meths-          hashExpr = IHashExpr hashEntries-      in (dictVar, hashExpr)+          methodEntries = map (makeHashEntry clsNm typNm) meths+      -- Add superclass dictionary references (Haskell-style nested dicts)+      superEntries <- makeSuperclassEntries clsNm typNm+      let hashExpr = IHashExpr (methodEntries ++ superEntries)+      return (dictVar, hashExpr)          makeHashEntry :: String -> String -> InstanceMethod -> (IExpr, IExpr)     makeHashEntry clsNm typNm (InstanceMethod methName _ _) =       let keyExpr = IConstantExpr (StringExpr (pack (sanitizeMethodName methName)))-          -- Reference to the method function           funcName = lowerFirst clsNm ++ typNm ++ capitalizeFirst (sanitizeMethodName methName)           valueExpr = IVarExpr funcName       in (keyExpr, valueExpr)++    makeSuperclassEntries :: String -> String -> EvalM [(IExpr, IExpr)]+    makeSuperclassEntries clsNm typNm = do+      classEnv <- getClassEnv+      case lookupClass clsNm classEnv of+        Just info -> return $ map (makeSuperEntry typNm) (classSupers info)+        Nothing   -> return []++    makeSuperEntry :: String -> String -> (IExpr, IExpr)+    makeSuperEntry typNm superName =+      let keyExpr = IConstantExpr (StringExpr (pack ("__super_" ++ superName)))+          superDictName = lowerFirst superName ++ typNm+          valueExpr = IVarExpr superDictName+      in (keyExpr, valueExpr)       -- Inductive declarations don't produce runtime code -- Constructor registration is handled by the type system desugarTopExpr (InductiveDecl _ _ _) = return Nothing +-- cas-type aliases and cas-subtype edges are fully handled during+-- environment building (Phase alpha/beta of the extensible tower);+-- no runtime artifact.+desugarTopExpr (DeclareCasType _ _) = return Nothing+desugarTopExpr (DeclareCasSubtype _ _) = return Nothing+desugarTopExpr (DeclareCasQuotient {}) = return Nothing  -- expanded before EnvBuilder+ -- Infix declarations don't produce runtime code desugarTopExpr (InfixDecl _ _) = return Nothing desugarTopExpr (PatternInductiveDecl _ _ _) = return Nothing  -- Handled in environment building phase  -- Pattern function declarations need type checking, so convert to IPatternFunctionDecl desugarTopExpr (PatternFunctionDecl name typeParams params retType body) = do-  let paramTypes = map (\(pname, pty) -> (pname, typeExprToType pty)) params-      retType' = typeExprToType retType+  aliasEnv <- getCasTypeAliasEnv+  let t2t = expandTypeAliases aliasEnv . typeExprToType+      paramTypes = map (\(pname, pty) -> (pname, t2t pty)) params+      retType' = t2t retType       tyVars = map TyVar typeParams   body' <- desugarPattern body   return . Just $ IPatternFunctionDecl name tyVars paramTypes retType' body'@@ -211,11 +240,591 @@ -- Symbol declarations desugarTopExpr (DeclareSymbol names mTypeExpr) = do   -- Convert type expression to type (defaults to Integer if not specified)+  aliasEnv <- getCasTypeAliasEnv   let ty = case mTypeExpr of-             Just texpr -> typeExprToType texpr+             Just texpr -> expandTypeAliases aliasEnv (typeExprToType texpr)              Nothing    -> typeExprToType TEInt+  -- Record the declaration order of CAS symbols for `declare ideal`+  -- (DG1: declared earlier = ranks lower = survives in normal forms).+  case ty of+    TInt       -> appendDeclaredSymbols names+    TMathValue -> appendDeclaredSymbols names+    _          -> return ()   return . Just $ IDeclareSymbol names (Just ty)+desugarTopExpr (DeclareRule mname level lhsPat rhs) = do+  -- Phase 7.5 (literal LHS) + Phase A (pattern variables `$x`, `#x`).+  --+  --   declare rule auto term i^2 = -1+  --   ⇒  def autoRule.<fresh> := \v -> applyTermRule i^2 (-1) v+  --+  -- With pattern variables (Phase A):+  --   declare rule trigPyth poly (sin $x)^2 + (cos #x)^2 = 1+  --   ⇒  def rule.trigPyth := \v -> match v as mathExpr with+  --                                  | (apply1 #sin $x)^2 + (apply1 #cos #x)^2 -> 1+  --                                  | _ -> v+  --+  -- Strategy:+  --   - If the LHS pattern contains no PatVar, take the literal-LHS path:+  --     reconstruct an Expr from the pattern and use `applyTermRule` so+  --     monomial-containment matching keeps working for term-level rules.+  --   - Otherwise, emit a `match v as <matcher> with | <pat> -> <rhs> | _ -> v`+  --     lambda. The user's surface syntax `f $x` (PApplyPat) is translated+  --     to `apply1 #f $x` so it matches mathExpr's matcher constructors.+  fr <- fresh+  let paramName = "__rule_input." ++ filter (\c -> c /= '$' && c /= '_') fr+  rhsI <- unNormalizeOps <$> desugar rhs+  let ruleTriggers = extractTriggerSymbols lhsPat+  body <- if patternHasPatVar lhsPat+            then buildPatternRuleBody paramName level lhsPat rhsI ruleTriggers+            else case patternToLiteralExpr lhsPat of+                   Just lhsExpr -> do+                     lhsI <- unNormalizeOps <$> desugar lhsExpr+                     buildLiteralRuleBody paramName level lhsI rhsI+                   Nothing ->+                     throwError $ Default+                       "declare rule LHS must be either a literal expression \+                       \or a pattern containing pattern variables ($x)."+  case mname of+    Just n -> do+      -- Named rule: emit `def rule.<n> := <body>` only.+      let varName = "rule." ++ n+      return . Just $ IDefine (stringToVar varName) body+    Nothing -> do+      -- Auto rule: emit two definitions:+      --   1. `def autoRule.<idx> := <body>` (the unwrapped rule lambda)+      --   2. `def mathNormalize := \v -> iterateRulesCAS [autoRule.0, ...]+      --                                                  (mathNormalizeBuiltin v)`+      --+      -- Triggers are stored in EvalState (already converted to `Set String`)+      -- and read inside iterateRulesCAS, so they aren't passed as arguments.+      appendAutoRuleTriggers ruleTriggers+      prevAutoNames <- getAutoRuleVarNames+      let autoVar    = "autoRule." ++ show (length prevAutoNames)+          allAutoVars = prevAutoNames ++ [autoVar]+      appendAutoRuleVarName autoVar+      mathNormBody <- buildMathNormalizeRedef allAutoVars+      return . Just $ IDefineMany+        [ (stringToVar autoVar, body)+        , (stringToVar "mathNormalize", mathNormBody)+        ]+  where+    -- Literal LHS path: \v -> applyTermRule lhs rhs v  (term-level)+    --                or \v -> if v = lhs then rhs else v  (poly/frac)+    buildLiteralRuleBody :: String -> RuleLevel -> IExpr -> IExpr -> EvalM IExpr+    buildLiteralRuleBody paramName lvl lhsI rhsI = case lvl of+      TermRuleLevel ->+        return $ ILambdaExpr Nothing [stringToVar paramName]+                   (IApplyExpr (IVarExpr "applyTermRule")+                               [lhsI, rhsI, IVarExpr paramName])+      _ ->+        return $ ILambdaExpr Nothing [stringToVar paramName]+                   (IIfExpr+                      (IApplyExpr (IVarExpr "=")+                                 [IVarExpr paramName, lhsI])+                      rhsI+                      (IVarExpr paramName)) +    -- Pattern-variable LHS path: emit a sub-expression-aware rule.+    -- The rule's "one step" lambda matches the user's pattern at a single+    -- value node (returning RHS on match, input otherwise). This single-step+    -- rule is then wrapped with the structural traversal primitive+    -- corresponding to the rule level:+    --   TermRuleLevel → mapTerm  (apply to each term/monomial)+    --   PolyRuleLevel → mapPoly  (apply at each (sub-)polynomial)+    --   FracRuleLevel → mapFrac  (apply at each (sub-)fraction)+    -- These primitives recurse into Apply1-4 / Quote / Function arguments+    -- automatically, so the rule fires at any sub-expression and iterates to+    -- a fixpoint at each visited node.+    buildPatternRuleBody :: String -> RuleLevel -> Pattern -> IExpr -> [String] -> EvalM IExpr+    buildPatternRuleBody paramName lvl pat rhsI triggers = do+      -- Use a separate fresh inner-arg name so the inner match is independent+      -- from the outer lambda parameter.+      frInner <- fresh+      let innerArg = "__rule_step." ++ filter (\c -> c /= '$' && c /= '_') frInner+          translated = wrapLhsForTermLevel (translateToMatcherPattern pat)+          mapPrim = case lvl of+            TermRuleLevel -> "mapTermAll"+            PolyRuleLevel -> "mapPolyAll"+            FracRuleLevel -> "mapFracAll"+          -- Inner one-step lambda built as Egison surface for desugar to handle+          -- the match-clause shape; we patch the RHS afterwards with rhsI.+          matchExpr = MatchExpr BFSMode+                        (VarExpr innerArg)+                        (VarExpr "mathExpr")+                        [(translated, ConstantExpr UndefinedExpr),+                         (WildCard,   VarExpr innerArg)]+      matchI <- desugar matchExpr+      let patchedMatchI = patchFirstMatchRhs matchI rhsI+          -- Per-term trigger guard: skip the matcher entirely on+          -- sub-values that contain none of the rule's trigger symbols.+          -- Sound with ANY-of semantics -- a sub-value the LHS can match+          -- necessarily contains the pattern's head symbol -- and cheap+          -- (one short-circuit scan vs. matcher startup).  The+          -- value-level filter in iterateRulesCAS already skips whole+          -- values; this covers the mixed case where only a few terms+          -- of a large value carry the trigger.+          guardedMatchI = case triggers of+            [] -> patchedMatchI+            _  -> IIfExpr+                    (IApplyExpr (IVarExpr "casContainsAnySymbol")+                      [ ICollectionExpr (map (IConstantExpr . StringExpr . pack) triggers)+                      , IVarExpr innerArg ])+                    patchedMatchI+                    (IVarExpr innerArg)+          oneStepLambda = ILambdaExpr Nothing [stringToVar innerArg] guardedMatchI+          mapCall       = IApplyExpr (IVarExpr mapPrim)+                                     [oneStepLambda, IVarExpr paramName]+      return $ ILambdaExpr Nothing [stringToVar paramName] mapCall++    -- Replace the first match clause's body in an IMatchExpr with the given+    -- IExpr. (We placeholder-desugar the match with `undefined`, then patch.)+    patchFirstMatchRhs :: IExpr -> IExpr -> IExpr+    patchFirstMatchRhs (IMatchExpr m tgt mtcher ((p, _) : rest)) newBody =+      IMatchExpr m tgt mtcher ((p, newBody) : rest)+    patchFirstMatchRhs e _ = e+-- G3 (design/cas-simplification.md): `declare ideal [g1, ..., gk]`.+--+--   declare ideal [w^2 + w + 1]+--     =>  def idealRules.N := idealTermRules [<priority atoms>] [g1', ..., gk']+--         def autoRule.N   := \v -> applyIdealRules idealRules.N v+--         def mathNormalize := ...   (same redefinition as declare rule auto)+--+-- The generators receive the same rule-free treatment as declare-rule+-- right-hand sides (unNormalizeOps), so generators that the active auto+-- rules would collapse are safe to write plainly.  The priority list is+-- the declaration order of `declare symbol` (DG1: earlier = survives),+-- extended by the compound atoms (symbolic applications, quotes) in the+-- order they appear in the generators.  idealTermRules computes the+-- reduced Groebner basis once (lazily, in the rule-free engine of+-- lib/math/algebra/groebner.egi -- the free-theory arithmetic is what+-- makes it safe to force the list while autoRule.N is already active)+-- and turns each element into a term-level rewrite pair (LT, LT - g).+desugarTopExpr (DeclareIdeal gens) = do+  gensI <- map unNormalizeOps <$> mapM desugar gens+  symOrder <- getDeclaredSymbolOrder+  let compounds = collectCompoundAtomExprs gensI+      piExprs   = map IVarExpr symOrder ++ compounds+      triggers  = concatMap iexprVarNames gensI+  appendAutoRuleTriggers triggers+  prevAutoNames <- getAutoRuleVarNames+  let idx         = show (length prevAutoNames)+      rulesVar    = "idealRules." ++ idx+      autoVar     = "autoRule." ++ idx+      allAutoVars = prevAutoNames ++ [autoVar]+  appendAutoRuleVarName autoVar+  let rulesBody = IApplyExpr (IVarExpr "idealTermRules")+                    [ICollectionExpr piExprs, ICollectionExpr gensI]+      ruleBody  = ILambdaExpr Nothing [stringToVar "v"]+                    (IApplyExpr (IVarExpr "applyIdealRules")+                                [IVarExpr rulesVar, IVarExpr "v"])+  mathNormBody <- buildMathNormalizeRedef allAutoVars+  return . Just $ IDefineMany+    [ (stringToVar rulesVar, rulesBody)+    , (stringToVar autoVar, ruleBody)+    , (stringToVar "mathNormalize", mathNormBody)+    ]++desugarTopExpr (DeclareDerivative name rhs) = do+  -- Phase 6.3 part 4-6: emit+  --   def deriv.<name> := <rhs>+  --   def chainPartialDiff := \v dx ->+  --       match v as mathValue with+  --         | apply1 #<n_1> $a -> deriv.<n_1> a *' chainPartialDiff a dx+  --         ...+  --         | apply1 #<n_k> $a -> deriv.<n_k> a *' chainPartialDiff a dx+  --         | _ -> chainPartialDiffBuiltin v dx+  --   where n_1..n_k are *all* the derivative names seen so far (including+  --   <name>). Each declare derivative redefines `chainPartialDiff` with the+  --   broader pattern set; Egison's name shadowing lets the latest+  --   definition win. The fallback uses `chainPartialDiffBuiltin` (defined in+  --   lib/math/analysis/derivative.egi and never redefined) so the+  --   recursion through nested mathfuncs terminates.+  rhsI <- desugar rhs+  -- Use only derivatives desugared *up to and including* this one, so the+  -- emitted chainPartialDiff body doesn't forward-reference `deriv.<later>`+  -- bindings. EnvBuilder pre-populates `derivativeRuleNames` with all names,+  -- but for code generation we want each declaration to reference only the+  -- names that have already been emitted.+  prevDesugared <- getDerivativesDesugared+  let allNames = prevDesugared ++ [name | name `notElem` prevDesugared]+  appendDerivativeDesugared name+  -- Build the chainPartialDiff body: a lambda over (v, dx) with a match.+  let derivBinding = (stringToVar ("deriv." ++ name), rhsI)+  chainBindingI <- buildChainPartialDiff allNames+  let chainBinding = (stringToVar "chainPartialDiff", chainBindingI)+  return . Just $ IDefineMany [derivBinding, chainBinding]+  where+    -- Build:+    --   \v dx -> match v as mathValue with+    --              | apply1 #<n1> $a -> deriv.<n1> a *' chainPartialDiff a dx+    --              ...+    --              | _ -> chainPartialDiffBuiltin v dx+    --+    -- We desugar a synthetic Egison expression rather than hand-building+    -- the IExpr tree, since match patterns and `apply1 #` are easier at+    -- the surface level.+    buildChainPartialDiff :: [String] -> EvalM IExpr+    buildChainPartialDiff names = do+      -- Recursive arm: deriv.<n> a *' partialDiff a dx.+      -- The recursive sub-call uses `partialDiff` (the typeclass method)+      -- so that the argument's runtime CAS shape decides which Differentiable+      -- instance handles it: this lets `partialDiff (sin (x^2)) x` decompose+      -- into `cos (x^2) * partialDiff (x^2) x = cos (x^2) * 2 x` correctly,+      -- because `partialDiff (x^2) x` dispatches to the Term instance.+      -- Nested mathfunc applications still work because partialDiff for+      -- Factor (apply1 _ _) routes through chainPartialDiff again.+      let mkClause n =+            ( InductivePat "apply1"+                 [ ValuePat (VarExpr n)+                 , PatVar "a"+                 ]+            , InfixExpr (Op "*'" 7 InfixL False)+                 (ApplyExpr (VarExpr ("deriv." ++ n)) [VarExpr "a"])+                 (ApplyExpr (VarExpr "partialDiff")+                            [VarExpr "a", VarExpr "dx"])+            )+          fallbackClause =+            ( WildCard+            , ApplyExpr (VarExpr "chainPartialDiffBuiltin") [VarExpr "v", VarExpr "dx"]+            )+          matchExpr = MatchExpr BFSMode+                        (VarExpr "v")+                        (VarExpr "mathValue")+                        (map mkClause names ++ [fallbackClause])+          lambda = LambdaExpr+                     [ Arg (APPatVar (VarWithIndices "v" []))+                     , Arg (APPatVar (VarWithIndices "dx" []))+                     ]+                     matchExpr+      desugar lambda+desugarTopExpr (DeclareMathFunc name _mType) = do+  -- Phase 6.3 part 5: emit a wrapper function that quotes the symbol on+  -- application:  def <name> (x : MathValue) : MathValue := '<name> x+  -- The parser builds `'name x` as `ApplyExpr (QuoteSymbolExpr (VarExpr name)) [VarExpr x]`,+  -- so we mirror that structure here.+  let body = LambdaExpr [Arg (APPatVar (VarWithIndices "x" []))]+                        (ApplyExpr (QuoteSymbolExpr (VarExpr name))+                                   [VarExpr "x"])+  bodyI <- desugar body+  return . Just $ IDefine (stringToVar name) bodyI+desugarTopExpr (DeclareApply name args body) = do+  -- Phase A: emit `def <name> := \<args> -> <body>` which overrides the+  -- wrapper generated by `declare mathfunc <name>`. Within the body, the+  -- user uses `'<name> arg` (quote) for the symbolic Factor fallback so+  -- recursion terminates; calling `<name> arg` (unquoted) re-enters this+  -- definition for further reduction.+  let argPats = map (Arg . APPatVar . (\n -> VarWithIndices n [])) args+      lam = LambdaExpr argPats body+  bodyI <- desugar lam+  return . Just $ IDefine (stringToVar name) bodyI++-- | Extract the names of literal symbols and functions referenced by a+-- declare-rule LHS pattern. Used to build a fast trigger-symbol filter so+-- the rule's body is only invoked when the input value contains at least+-- one of these names.+--+-- Examples:+--   i^2                  → ["i"]+--   (sqrt $a)^2          → ["sqrt"]+--   log (exp $n)         → ["log", "exp"]+--   $x ^ 3               → []   (no literal symbols, rule must always run)+--+-- An empty result means the rule should NOT be guarded (it might match any+-- value), so the desugarer should skip the wrapper in that case.+extractTriggerSymbols :: Pattern -> [String]+extractTriggerSymbols = nub . go+ where+  go (ValuePat e)        = exprNames e+  -- Only the HEAD of an application pattern is a trigger: a term+  -- matching `log (exp $n)` necessarily contains a log-application+  -- factor, so "log" alone suffices, and a smaller trigger set lets+  -- the rule be skipped on more values (e.g. exp-heavy values never+  -- attempt the log rule).  Argument symbols are implied, not needed.+  go (PApplyPat f _)     = exprNames f+  go (DApplyPat p ps)    = go p ++ concatMap go ps+  go (InfixPat _ a b)    = go a ++ go b+  go (AndPat a b)        = go a ++ go b+  go (OrPat a b)         = go a ++ go b+  go (ForallPat a b)     = go a ++ go b+  go (NotPat p)          = go p+  go (TuplePat ps)       = concatMap go ps+  go (InductivePat _ ps) = concatMap go ps+  go (InductiveOrPApplyPat _ ps) = concatMap go ps+  go (IndexedPat p _)    = go p+  go (LetPat _ p)        = go p+  go (LoopPat _ _ a b)   = go a ++ go b+  go (SeqConsPat a b)    = go a ++ go b+  go _                   = []++  -- An expression appearing in a literal position contributes its symbol+  -- names. We only descend into shapes where a "trigger symbol" sense+  -- exists; opaque sub-expressions (lambdas, lets, etc.) contribute none.+  exprNames :: Expr -> [String]+  exprNames (VarExpr n)          = [n]+  exprNames (QuoteSymbolExpr e)  = exprNames e+  exprNames (InfixExpr _ a b)    = exprNames a ++ exprNames b+  -- Same head-only refinement as PApplyPat above.+  exprNames (ApplyExpr f _)      = exprNames f+  -- Operator section like `(^)` or `(+ 1)`: the operator name itself is+  -- the trigger (e.g. `apply2 #(^) ...` should trigger only on values+  -- containing the `^` function).+  exprNames (SectionExpr op ml mr) = [repr op] ++ maybe [] exprNames ml ++ maybe [] exprNames mr+  exprNames _                    = []++  nub = go' []+   where+    go' acc []     = reverse acc+    go' acc (x:xs) | x `elem` acc = go' acc xs+                   | otherwise    = go' (x:acc) xs++-- | Detect whether a Pattern has any PatVar at any depth.+patternHasPatVar :: Pattern -> Bool+patternHasPatVar (PatVar _)        = True+patternHasPatVar (ValuePat _)      = False+patternHasPatVar WildCard          = False+patternHasPatVar (PredPat _)       = False+patternHasPatVar ContPat           = False+patternHasPatVar LaterPatVar       = False+patternHasPatVar (NotPat p)        = patternHasPatVar p+patternHasPatVar (AndPat a b)      = patternHasPatVar a || patternHasPatVar b+patternHasPatVar (OrPat a b)       = patternHasPatVar a || patternHasPatVar b+patternHasPatVar (ForallPat a b)   = patternHasPatVar a || patternHasPatVar b+patternHasPatVar (TuplePat ps)     = any patternHasPatVar ps+patternHasPatVar (InductivePat _ ps)            = any patternHasPatVar ps+patternHasPatVar (InductiveOrPApplyPat _ ps)    = any patternHasPatVar ps+patternHasPatVar (InfixPat _ a b)  = patternHasPatVar a || patternHasPatVar b+patternHasPatVar (IndexedPat p _)  = patternHasPatVar p+patternHasPatVar (LetPat _ p)      = patternHasPatVar p+patternHasPatVar (LoopPat _ _ a b) = patternHasPatVar a || patternHasPatVar b+patternHasPatVar (PApplyPat _ ps)  = any patternHasPatVar ps+patternHasPatVar (DApplyPat p ps)  = patternHasPatVar p || any patternHasPatVar ps+patternHasPatVar (VarPat _)        = False+patternHasPatVar (SeqConsPat a b)  = patternHasPatVar a || patternHasPatVar b+patternHasPatVar SeqNilPat         = False++-- | Convert a literal pattern (no PatVar) back to an Expr so we can desugar+-- it via the existing applyTermRule path. Returns Nothing if the pattern+-- contains constructs that don't have an Expr equivalent.+patternToLiteralExpr :: Pattern -> Maybe Expr+patternToLiteralExpr (ValuePat e)         = Just e+patternToLiteralExpr (InfixPat op a b)    = do+  ae <- patternToLiteralExpr a+  be <- patternToLiteralExpr b+  return $ InfixExpr op ae be+patternToLiteralExpr (PApplyPat f args)   = do+  argExprs <- mapM patternToLiteralExpr args+  return $ ApplyExpr f argExprs+patternToLiteralExpr (TuplePat ps)        = do+  es <- mapM patternToLiteralExpr ps+  return $ TupleExpr es+patternToLiteralExpr (InductivePat _ _)   = Nothing+patternToLiteralExpr WildCard             = Nothing+patternToLiteralExpr (PatVar _)           = Nothing+patternToLiteralExpr _                    = Nothing++-- | Translate a user-written rule LHS pattern into a pattern that uses+-- mathExpr/multExpr matcher constructors. Specifically, surface syntax+-- `f $x` (PApplyPat with a VarExpr/QuoteSymbolExpr function) is rewritten+-- to `apply1 #f $x` (InductivePat using mathExpr's apply1 clause).+translateToMatcherPattern :: Pattern -> Pattern+translateToMatcherPattern p = case p of+  PApplyPat funcExpr args ->+    let funcName = case funcExpr of+                     VarExpr n                   -> Just n+                     QuoteSymbolExpr (VarExpr n) -> Just n+                     _                            -> Nothing+        translatedArgs = map translateToMatcherPattern args+    in case (funcName, length translatedArgs) of+         (Just _, 1) ->+           InductivePat "apply1" (ValuePat funcExpr : translatedArgs)+         (Just _, 2) ->+           InductivePat "apply2" (ValuePat funcExpr : translatedArgs)+         (Just _, 3) ->+           InductivePat "apply3" (ValuePat funcExpr : translatedArgs)+         (Just _, 4) ->+           InductivePat "apply4" (ValuePat funcExpr : translatedArgs)+         _ -> PApplyPat funcExpr translatedArgs+  InfixPat op a b ->+    InfixPat op (translateToMatcherPattern a) (translateToMatcherPattern b)+  InductivePat n args ->+    InductivePat n (map translateToMatcherPattern args)+  TuplePat ps -> TuplePat (map translateToMatcherPattern ps)+  AndPat a b  -> AndPat (translateToMatcherPattern a) (translateToMatcherPattern b)+  OrPat a b   -> OrPat (translateToMatcherPattern a) (translateToMatcherPattern b)+  NotPat q    -> NotPat (translateToMatcherPattern q)+  IndexedPat q es -> IndexedPat (translateToMatcherPattern q) es+  _ -> p++-- | Top-level wrap of a translated rule LHS to enable multi-factor+-- decomposition. When the outer pattern is `f $x * g $y * ...` (a chain of+-- factor-shaped operands), the mathValue matcher's `$ * $` decomposes into+-- (coeff, monomial-as-multExpr) — not (factor, rest). To access the+-- (factor, integer, multExpr) decomposition path that handles apply1-4 via+-- the `factor` matcher, we must enter the multExpr context. The wrap is:+--+--   f $x * g $y+--     becomes+--   mult _ (((f $x) ^ #1) * ((g $y) ^ #1))+--+-- This is applied only at the TOP LEVEL (not inside other patterns) so that+-- patterns like `exp ($n * i * π)` are unaffected (the inner `$n * i * π`+-- is matched as the apply1 argument's mathValue, where (coeff, monomial)+-- decomposition is the right semantics).+wrapLhsForTermLevel :: Pattern -> Pattern+wrapLhsForTermLevel p+  | isMultChainOfFactorShapes p =+      InductivePat "mult" [WildCard, wrapFactorsInChain p]+  | otherwise = p++-- | Recursively wrap factor-shaped patterns inside a `*` chain with `^ #1`.+-- Operates only on top-level `*` chains — sub-patterns inside an apply+-- argument keep their original shape.+wrapFactorsInChain :: Pattern -> Pattern+wrapFactorsInChain (InfixPat op a b) | repr op == "*" =+  InfixPat op (wrapFactorsInChain a) (wrapFactorsInChain b)+wrapFactorsInChain p+  | isFactorShaped p =+      InfixPat (Op "^" 9 InfixN False) p+               (ValuePat (ConstantExpr (IntegerExpr 1)))+  | otherwise = p++-- | True if a pattern is an `*`-chain whose leaves are all factor-shaped+-- (apply1-4 / symbol / quote / func / a literal symbol via ValuePat VarExpr).+isMultChainOfFactorShapes :: Pattern -> Bool+isMultChainOfFactorShapes (InfixPat op a b) | repr op == "*" =+  isMultChainOfFactorShapes a && isMultChainOfFactorShapes b+isMultChainOfFactorShapes p = isFactorShaped p++-- | True if a pattern names a single CAS factor (apply1-4, symbol, etc.).+isFactorShaped :: Pattern -> Bool+isFactorShaped (InductivePat name _) =+  name `elem` ["apply1", "apply2", "apply3", "apply4", "symbol", "quote", "func"]+isFactorShaped (ValuePat (VarExpr _)) = True+isFactorShaped _ = False++-- | Replace normalizing arithmetic operators (`+`, `-`, `*`, `/`, `^`) with+-- their **primitive** Haskell-level counterparts. Used by `declare rule auto`+-- to avoid infinite recursion:+--+-- The lib's un-normalized operators (`+'`, `-'`, etc.) are direct aliases of+-- `i.+`/`i.-`/etc. But the lib's `power'` (used by `^'`) iterates via+-- `take`/`foldl`, which use `-` (subtraction) on Integer. Subtraction at the+-- MathValue level dispatches to `minusForMathValue`, which calls+-- `mathNormalize`, creating a cycle:+--   `mathNormalize` → rule body → `power' p 2` → `take 2 ...` → `n - 1`+--   → `mathNormalize` → ...+--+-- Bypassing this requires using the primitives **directly** in the rule body.+-- For `^` (power), the primitive `i.power` only works on integer arguments,+-- so we instead expand `x ^ n` (where `n` is a positive integer literal) into+-- repeated `i.* x x ... x` calls. This works for symbolic CAS values.+unNormalizeOps :: IExpr -> IExpr+unNormalizeOps e = case e of+  -- Special case: `x ^ n` where n is a literal positive integer.+  -- Expand to nested i.* (works for symbolic CAS values).+  IApplyExpr (IVarExpr "^")+             [base, IConstantExpr (IntegerExpr n)]+    | n >= 1 ->+        let base' = unNormalizeOps base+            mulPrim = IVarExpr "i.*"+            go 1 = base'+            go k = IApplyExpr mulPrim [base', go (k - 1)]+        in go n+  -- General case: `x ^ n` with non-literal n. Dispatch to `^'`, the+  -- un-normalised power operator that uses `power'` (which now uses+  -- direct recursion + i.- instead of take/foldl). This avoids the+  -- mathNormalize cycle when ^ appears in a declare-rule RHS.+  IApplyExpr (IVarExpr "^") [base, expn] ->+    IApplyExpr (IVarExpr "^'") [unNormalizeOps base, unNormalizeOps expn]+  IApplyExpr (IVarExpr nm) args+    | Just nm' <- lookup nm opTable ->+        IApplyExpr (IVarExpr nm') (map unNormalizeOps args)+  IApplyExpr f args ->+    IApplyExpr (unNormalizeOps f) (map unNormalizeOps args)+  ILambdaExpr mn vs body ->+    ILambdaExpr mn vs (unNormalizeOps body)+  IIfExpr c t f ->+    IIfExpr (unNormalizeOps c) (unNormalizeOps t) (unNormalizeOps f)+  ILetExpr bindings body ->+    ILetExpr [(p, unNormalizeOps b) | (p, b) <- bindings] (unNormalizeOps body)+  ITupleExpr es ->+    ITupleExpr (map unNormalizeOps es)+  ICollectionExpr es ->+    ICollectionExpr (map unNormalizeOps es)+  IConsExpr a b ->+    IConsExpr (unNormalizeOps a) (unNormalizeOps b)+  IJoinExpr a b ->+    IJoinExpr (unNormalizeOps a) (unNormalizeOps b)+  IInductiveDataExpr nm es ->+    IInductiveDataExpr nm (map unNormalizeOps es)+  IQuoteSymbolExpr e' ->+    IQuoteSymbolExpr (unNormalizeOps e')+  -- Other constructors: leave as-is (including IVarExpr, IConstantExpr,+  -- patterns, matcher refs, etc.). They don't contain operators.+  _ -> e+  where+    opTable :: [(String, String)]+    opTable =+      [ ("+", "i.+")+      , ("-", "i.-")+      , ("*", "i.*")+      , ("/", "i./")+      ]++-- Build the body of the redefined `mathNormalize`:+--   \v -> iterateRulesCAS [autoRule.0, ..., autoRule.N]+--                         (mathNormalizeBuiltin v)+-- iterateRulesCAS reads trigger sets from EvalState (cached as+-- [Set String] at desugar time) and runs the rule-application ++-- fixpoint loop, scanning the value once per iteration to skip any+-- rule whose trigger set is disjoint from the value's symbols.+-- Shared by `declare rule auto` and `declare ideal`.+buildMathNormalizeRedef :: [String] -> EvalM IExpr+buildMathNormalizeRedef autoVars = do+  let rulesList = ICollectionExpr $ map IVarExpr autoVars+      mathBuiltinCall = IApplyExpr (IVarExpr "mathNormalizeBuiltin") [IVarExpr "v"]+      iterCall = IApplyExpr (IVarExpr "iterateRulesCAS") [rulesList, mathBuiltinCall]+  return $ ILambdaExpr Nothing [stringToVar "v"] iterCall++-- Candidate compound atoms (symbolic applications, quoted expressions)+-- of desugared ideal generators, in appearance order (deduplicated).+-- They extend the declaration-order priority list: compound atoms are+-- not declared with `declare symbol`, so their rank comes from where+-- they first appear in the generators (DG1, cas-simplification 3.5).+collectCompoundAtomExprs :: [IExpr] -> [IExpr]+collectCompoundAtomExprs es = nubBy (\a b -> show a == show b) (concatMap go es)+  where+    structuralOps =+      ["i.+", "i.-", "i.*", "i./", "+'", "-'", "*'", "/'", "^'", "power", "power'"]+    go e = case e of+      IApplyExpr (IVarExpr nm) args+        | nm `elem` structuralOps -> concatMap go args+        | otherwise               -> e : concatMap go args+      IApplyExpr f args  -> go f ++ concatMap go args+      IQuoteExpr _       -> [e]+      ITupleExpr xs      -> concatMap go xs+      ICollectionExpr xs -> concatMap go xs+      IConsExpr a b      -> go a ++ go b+      IJoinExpr a b      -> go a ++ go b+      _                  -> []++-- All variable names occurring in an IExpr.  Conservative superset used+-- for the trigger-symbol set of `declare ideal` (extra names only make+-- the rule fire more often, never less).+iexprVarNames :: IExpr -> [String]+iexprVarNames e = case e of+  IVarExpr nm         -> [nm]+  IApplyExpr f args   -> iexprVarNames f ++ concatMap iexprVarNames args+  IQuoteExpr a        -> iexprVarNames a+  IQuoteSymbolExpr a  -> iexprVarNames a+  ITupleExpr xs       -> concatMap iexprVarNames xs+  ICollectionExpr xs  -> concatMap iexprVarNames xs+  IConsExpr a b       -> iexprVarNames a ++ iexprVarNames b+  IJoinExpr a b       -> iexprVarNames a ++ iexprVarNames b+  ILambdaExpr _ _ b   -> iexprVarNames b+  IIfExpr a b c       -> iexprVarNames a ++ iexprVarNames b ++ iexprVarNames c+  _                   -> []+ -- | Convert TypedParam to Arg ArgPattern for lambda expressions typedParamToArgPattern :: TypedParam -> Arg ArgPattern typedParamToArgPattern (TPVar pname _) =@@ -604,17 +1213,43 @@ desugar (QuoteExpr expr) =   IQuoteExpr <$> desugar expr -desugar (QuoteSymbolExpr expr) =-  IQuoteSymbolExpr <$> desugar expr+-- `'e` has two meanings, discriminated by the desugared inner form:+--   * `'f` (a variable, including operator sections like `'(^)`):+--     quote the function/symbol itself (QuoteSymbolExpr semantics).+--   * `'(expr)` (anything else): the rule-suppression quote -- build+--     expr with the rule-free structural arithmetic (i.+, i.*, ...),+--     the same treatment declare-rule right-hand sides receive.+--     `declare rule` rewriting does not fire inside, so ideal+--     generators such as '((sin θ)^2 + (cos θ)^2 - 1) survive+--     construction instead of collapsing under their own auto rules.+desugar (QuoteSymbolExpr expr) = do+  e <- desugar expr+  case e of+    IVarExpr _ -> return $ IQuoteSymbolExpr e+    _          -> return $ unNormalizeOps e  desugar (WedgeApplyExpr expr args) =   IWedgeApplyExpr <$> desugar expr <*> mapM desugar args  desugar (FunctionExpr args) = return $ IFunctionExpr args --- Type annotation is erased at runtime-desugar (TypeAnnotation expr _typeExpr) = desugar expr+-- Type annotation `(e : T)` desugars to `IReshape T (desugar e)` so that+-- the type checker validates `e`'s inferred type against `T` and the+-- evaluator structurally rewrites the runtime CAS value to fit `T`. For+-- non-CAS types `T`, the eval handler is a no-op (passes the value through).+desugar (TypeAnnotation expr typeExpr) = do+  inner <- desugar expr+  aliasEnv <- getCasTypeAliasEnv+  return $ IReshape (expandTypeAliases aliasEnv (typeExprToType typeExpr)) inner +-- `simplify <expr> using <ruleName>` (Phase 7.6).+-- Desugars to a direct call of the registered rule lambda:+--   simplify e using r  ⇒  rule.r e+-- The rule lambda was emitted by `desugarTopExpr (DeclareRule (Just r) ...)`.+desugar (SimplifyUsingExpr body ruleName) = do+  bodyI <- desugar body+  return $ IApplyExpr (IVarExpr ("rule." ++ ruleName)) [bodyI]+ -- Typed lambda is desugared to regular lambda desugar (TypedLambdaExpr params _retType body) = do   let args = map (\(name, _) -> Arg (APPatVar (VarWithIndices name []))) params@@ -836,7 +1471,8 @@ extractIndexExpr _                = error "extractIndexExpr: Not supported"  isExtendedIndice :: VarIndex -> Bool-isExtendedIndice VSubscript{}       = False-isExtendedIndice VSuperscript{}     = False-isExtendedIndice (VGroupScripts xs) = isExtendedIndice (head xs)-isExtendedIndice _                  = True+isExtendedIndice VSubscript{}            = False+isExtendedIndice VSuperscript{}          = False+isExtendedIndice (VGroupScripts (x:_))   = isExtendedIndice x+isExtendedIndice (VGroupScripts [])      = True+isExtendedIndice _                       = True
hs-src/Language/Egison/EnvBuilder.hs view
@@ -20,23 +20,27 @@   , EnvBuildResult(..)   ) where -import           Control.Monad              (foldM)+import           Control.Monad              (foldM, when) import           Control.Monad.Except       (throwError)-import           Control.Monad.State-import           Data.Char                  (toUpper, toLower)+import           Control.Monad.IO.Class     (liftIO)+import           Data.Char                  (isUpper) import qualified Data.HashMap.Strict        as HashMap+import           System.IO                  (hPutStrLn, stderr)  import           Language.Egison.AST-import           Language.Egison.Data       (EvalM)-import           Language.Egison.EvalState  (ConstructorInfo(..), ConstructorEnv, PatternConstructorEnv)-import           Language.Egison.IExpr      (Var(..), Index(..), stringToVar)+import           Language.Egison.Data       (EvalM, EgisonError(..))+import           Language.Egison.EvalState  (MonadEval(getConstructorEnv, getCasTypeAliasEnv, getCasSubtypeEdges), ConstructorInfo(..), ConstructorEnv, PatternConstructorEnv)+import           Language.Egison.Type.Pretty (prettyType)+import qualified Language.Egison.Type.Subtype as Subtype+import           Language.Egison.IExpr      (Var(..), stringToVar) import           Language.Egison.Desugar    (transVarIndex) import           Language.Egison.Type.Env   (TypeEnv, ClassEnv, PatternTypeEnv, emptyEnv, emptyClassEnv, emptyPatternEnv,                                              extendEnv, extendPatternEnv, addClass, addInstance, lookupClass) import qualified Language.Egison.Type.Types as Types-import           Language.Egison.Type.Types (Type(..), TyVar(..), Constraint(..), TypeScheme(..), TensorShape(..),-                                             ClassInfo, InstanceInfo, freeTyVars, typeToName, sanitizeMethodName, typeExprToType,+import           Language.Egison.Type.Types (Type(..), TyVar(..), Constraint(..), TypeScheme(..),+                                             freeTyVars, sanitizeMethodName, typeExprToType,                                              capitalizeFirst, lowerFirst)+import           Language.Egison.Type.Subst (emptySubst, singletonSubst, composeSubst, applySubst) import qualified Data.Set as Set  -- | Result of environment building phase@@ -46,6 +50,27 @@   , ebrConstructorEnv :: ConstructorEnv  -- ^ Data constructor information   , ebrPatternConstructorEnv :: PatternConstructorEnv  -- ^ Pattern constructor information   , ebrPatternTypeEnv :: PatternTypeEnv  -- ^ Pattern function information+  -- Phase 7.4/7.5: collected `declare rule` declarations. Stored as the raw+  -- (name, level, lhs, rhs) tuple. Rule application is not yet wired into+  -- normalization; this field exists so the data round-trips through env+  -- building and is available for inspection / future Phase 7.5 code.+  , ebrReductionRules :: [(Maybe String, RuleLevel, Pattern, Expr)]+  -- Phase 6.3: collected `declare derivative` declarations. Maps function+  -- name -> derivative expression. Wiring into `Differentiable Factor` is+  -- still pending; for now, just the registration list.+  , ebrDerivativeRules :: [(String, Expr)]+  -- Names of functions declared via `declare mathfunc`. Used by the+  -- DeclareApply handler to enforce that `declare apply foo ...` only+  -- appears after a corresponding `declare mathfunc foo`.+  , ebrMathFuncNames :: Set.Set String+  -- Phase alpha (extensible CAS tower): `declare cas-type` aliases declared+  -- in THIS batch, name -> fully expanded Type. Merged into the persistent+  -- EvalState alias env by the caller (Eval.buildAndMergeEnvironments).+  , ebrCasTypeAliases :: HashMap.HashMap String Type+  -- Phase beta: `declare cas-subtype` edges declared in THIS batch+  -- (alias-expanded, D1-checked, redundant ones included). Appended to the+  -- persistent EvalState edge list by the caller.+  , ebrCasSubtypeEdges :: [(Type, Type)]   } deriving (Show)  --------------------------------------------------------------------------------@@ -57,6 +82,42 @@ -- It must be called AFTER expandLoads (Phase 1) and BEFORE type inference (Phase 5). buildEnvironments :: [TopExpr] -> EvalM EnvBuildResult buildEnvironments exprs = do+  -- Names of value-level inductive types: this batch's `inductive` declarations PLUS the+  -- types already registered by earlier load units (the accumulated constructor env).+  -- Cross-batch coverage matters because a matcher defined in file B over a type declared+  -- in file A must still keep that type concrete in its signature (see concretizeDeclaredTypes);+  -- B's TopExpr list alone would not mention `inductive A`.+  priorCtorEnv <- getConstructorEnv+  priorAliases <- getCasTypeAliasEnv+  let declaredTypes = Set.fromList ([ n | InductiveDecl n _ _ <- exprs ]+                                    ++ [ ctorTypeName ci | ci <- HashMap.elems priorCtorEnv ])++  -- Phase alpha (extensible CAS tower): collect `declare cas-type` aliases+  -- first (prepass, so declaration order does not matter for users of the+  -- alias), then resolve alias-in-alias references to a fixpoint. Bodies are+  -- stored fully expanded so a single substitution pass suffices at use sites.+  newAliasesRaw <- foldM (collectCasTypeAlias declaredTypes priorAliases)+                         HashMap.empty+                         [ (n, te) | DeclareCasType n te <- exprs ]+  newAliases <- resolveCasTypeAliases priorAliases newAliasesRaw+  -- Restriction on open atom sets, checked on the fully expanded alias+  -- bodies (alias-in-alias expansion can only be judged after resolution):+  -- a nested Poly tower may contain at most one [..]+  -- (Types.hasAmbiguousOpenTower; the runtime reshape's atom routing would+  -- otherwise be ambiguous).+  mapM_ (\(n, t) ->+          when (Types.hasAmbiguousOpenTower t) $ throwError $ Default $+            "declare cas-type " ++ n ++ ": at most one open atom set [..] " +++            "may appear in a nested Poly tower: " ++ prettyType t)+        (HashMap.toList newAliases)+  let aliasEnv = HashMap.union newAliases priorAliases++  -- Phase beta: collect `declare cas-subtype` edges (alias-expanded) and run+  -- the D1 join-semilattice check per edge, in declaration order.+  priorEdges <- getCasSubtypeEdges+  newEdges <- foldM (collectCasSubtypeEdge aliasEnv priorEdges) []+                    [ (l, r) | DeclareCasSubtype l r <- exprs ]+   -- Start with empty environments   let initialResult = EnvBuildResult         { ebrTypeEnv = emptyEnv@@ -64,76 +125,208 @@         , ebrConstructorEnv = HashMap.empty         , ebrPatternConstructorEnv = emptyPatternEnv         , ebrPatternTypeEnv = emptyPatternEnv+        , ebrReductionRules = []+        , ebrDerivativeRules = []+        , ebrMathFuncNames = Set.empty+        , ebrCasTypeAliases = newAliases+        , ebrCasSubtypeEdges = newEdges         }-  +   -- Process each top-level expression to collect declarations-  foldM processTopExpr initialResult exprs+  foldM (processTopExpr declaredTypes aliasEnv) initialResult exprs --- | Process a single top-level expression to collect environment information-processTopExpr :: EnvBuildResult -> TopExpr -> EvalM EnvBuildResult-processTopExpr result topExpr = case topExpr of-  +-- | Validate and register a single `declare cas-type` alias.+-- Rules (design/type-cas-tower-implementation.md section 2):+--   * the alias name must be capitalized+--   * it must not clash with builtin type names, declared inductive types,+--     or an existing alias (no redeclaration)+--   * the body may reference previously declared aliases only (a leftover+--     alias name after expansion means a self/forward reference)+collectCasTypeAlias :: Set.Set String -> HashMap.HashMap String Type+                    -> HashMap.HashMap String Type -> (String, TypeExpr)+                    -> EvalM (HashMap.HashMap String Type)+collectCasTypeAlias declaredTypes priorAliases acc (name, te) = do+  when (not (startsUpper name)) $ throwError $ Default $+    "declare cas-type: alias name must be capitalized: " ++ name+  when (Set.member name Types.reservedCasTypeNames) $ throwError $ Default $+    "declare cas-type: alias name clashes with a builtin type: " ++ name+  when (Set.member name declaredTypes) $ throwError $ Default $+    "declare cas-type: alias name clashes with an inductive type: " ++ name+  -- A nominal entry `name -> TInductive name []` in the alias env is a+  -- cas-quotient type (registered by Eval.expandCasQuotientDecls).+  case HashMap.lookup name priorAliases of+    Just (TInductive n []) | n == name ->+      throwError $ Default $+        "declare cas-type: name is already a cas-quotient type: " ++ name+    _ -> return ()+  when (HashMap.member name priorAliases || HashMap.member name acc) $+    throwError $ Default $+      "declare cas-type: alias is already declared: " ++ name+  return (HashMap.insert name (typeExprToType te) acc)+  where+    startsUpper (c:_) = isUpper c+    startsUpper _     = False++-- | Validate and register a `declare cas-subtype A ⊂ B` edge (Phase beta;+-- D1 declare-time semilattice check, design/type-cas-tower.md §8 D1).+-- Redundant edges are stored anyway — their endpoints then participate in+-- the node set of later checks — with a warning.+collectCasSubtypeEdge :: HashMap.HashMap String Type -> [(Type, Type)]+                      -> [(Type, Type)] -> (TypeExpr, TypeExpr)+                      -> EvalM [(Type, Type)]+collectCasSubtypeEdge aliasEnv priorEdges acc (lhsTE, rhsTE) = do+  let lhs = Types.expandTypeAliases aliasEnv (typeExprToType lhsTE)+      rhs = Types.expandTypeAliases aliasEnv (typeExprToType rhsTE)+      edges = priorEdges ++ acc+      pp = prettyType+  when (not (Subtype.isCasType lhs) || not (Subtype.isCasType rhs)) $+    throwError $ Default $+      "declare cas-subtype: both sides must be CAS types: " +++      pp lhs ++ " <: " ++ pp rhs+  mapM_ (\side ->+          when (Types.hasAmbiguousOpenTower side) $ throwError $ Default $+            "declare cas-subtype: at most one open atom set [..] may " +++            "appear in a nested Poly tower: " ++ pp side)+        [lhs, rhs]+  case Subtype.checkEdgeAddition edges (lhs, rhs) of+    Subtype.EdgeCycle -> throwError $ Default $+      "declare cas-subtype " ++ pp lhs ++ " <: " ++ pp rhs +++      ": the reverse relation already holds; adding this edge would " +++      "collapse the two types into one order point (cycle)"+    Subtype.EdgeAmbiguous witnesses -> throwError $ Default $+      "declare cas-subtype " ++ pp lhs ++ " <: " ++ pp rhs +++      ": join would become ambiguous (D1 semilattice check).\n" +++      concatMap (\(x, y, j) ->+        "  pair (" ++ pp x ++ ", " ++ pp y ++ ") would get minimal upper bounds {" +++        pp j ++ ", " ++ pp rhs ++ "}\n" +++        "  hint: declare the completing edge first:\n" +++        "    declare cas-subtype " ++ pp j ++ " <: " ++ pp rhs ++ "\n")+        witnesses+    Subtype.EdgeRedundant -> do+      liftIO $ hPutStrLn stderr $+        "Warning: declare cas-subtype " ++ pp lhs ++ " <: " ++ pp rhs +++        " is already derivable (redundant edge)"+      return (acc ++ [(lhs, rhs)])+    Subtype.EdgeRefines witnesses -> do+      liftIO $ hPutStrLn stderr $+        "Warning: declare cas-subtype " ++ pp lhs ++ " <: " ++ pp rhs +++        " refines existing joins (values unchanged, static types get more precise):" +++        concatMap (\(x, y, j) ->+          "\n  join(" ++ pp x ++ ", " ++ pp y ++ "): " ++ pp j ++ " -> " ++ pp rhs)+          witnesses+      return (acc ++ [(lhs, rhs)])+    Subtype.EdgeOk -> return (acc ++ [(lhs, rhs)])++-- | Resolve alias-in-alias references to a fixpoint, so aliases may refer to+-- each other regardless of declaration order (prepass semantics, matching+-- the other `declare` families). Each round substitutes one nesting level;+-- a definition that keeps growing past |aliases| + 1 rounds is cyclic.+resolveCasTypeAliases :: HashMap.HashMap String Type -> HashMap.HashMap String Type+                      -> EvalM (HashMap.HashMap String Type)+resolveCasTypeAliases priorAliases = go (0 :: Int)+  where+    go rounds m+      | m' == m = return m+      | rounds > HashMap.size m + 1 =+          throwError $ Default $+            "declare cas-type: cyclic alias definition among: " +++            unwords (HashMap.keys m)+      | otherwise = go (rounds + 1) m'+      where+        full = HashMap.union m priorAliases+        m'   = HashMap.map (Types.expandTypeAliases full) m++-- | Rewrite bare declared-inductive-type names that 'typeExprToType' produced as+-- type variables (e.g. @Matcher Nat@ parses to @TMatcher (TVar "Nat")@) into the+-- concrete @TInductive@.  Without this, an explicit signature @nat : Matcher Nat@+-- is generalized to @forall a. Matcher a@, so a recursive matcher's self-reference+-- instantiates to a fresh inner type and fails the MatcherSlot structural check.+-- Only names declared via @inductive@ in this batch are rewritten; undeclared+-- capitalized names (e.g. a stale @MathExpr@) stay type variables.+concretizeDeclaredTypes :: Set.Set String -> Type -> Type+concretizeDeclaredTypes decls = applySubst subst+  where subst = foldr (\n s -> composeSubst (singletonSubst (TyVar n) (TInductive n [])) s)+                      emptySubst (Set.toList decls)++-- | Process a single top-level expression to collect environment information.+-- `aliasEnv` carries `declare cas-type` aliases (prior batches + this batch);+-- every TypeExpr conversion goes through `t2t` so alias names are expanded+-- before types are stored anywhere (Phase alpha of the extensible tower).+processTopExpr :: Set.Set String -> HashMap.HashMap String Type -> EnvBuildResult -> TopExpr -> EvalM EnvBuildResult+processTopExpr declaredTypes aliasEnv result topExpr = case topExpr of+   -- 1. Data Constructor Definitions (from InductiveDecl)   InductiveDecl typeName typeParams constructors -> do     let typeParamVars = map (TVar . TyVar) typeParams         adtType = TInductive typeName typeParamVars         typeEnv = ebrTypeEnv result         ctorEnv = ebrConstructorEnv result-    +     -- Register each constructor-    (typeEnv', ctorEnv') <- foldM (registerConstructor typeName typeParams adtType) -                                   (typeEnv, ctorEnv) +    (typeEnv', ctorEnv') <- foldM (registerConstructor aliasEnv typeName typeParams adtType)+                                   (typeEnv, ctorEnv)                                    constructors          return result { ebrTypeEnv = typeEnv', ebrConstructorEnv = ctorEnv' }   -  -- 2. Type Class Definitions (from ClassDeclExpr)-  ClassDeclExpr (ClassDecl className [typeParam] superClasses methods) -> do+  -- 2. Type Class Definitions (from ClassDeclExpr).+  -- Supports any number of type parameters (single-param `class Eq a` and+  -- multi-param `class Embed a b` go through the same path). Methods are still+  -- registered against the *first* parameter for backward compatibility with+  -- existing single-param infrastructure (Phase 5.5 multi-param-aware+  -- elaboration is a separate task).+  ClassDeclExpr (ClassDecl className typeParams superClasses methods) | not (null typeParams) -> do     let classEnv = ebrClassEnv result         typeEnv = ebrTypeEnv result-        tyVar = TyVar typeParam-        +        tyVars = map TyVar typeParams+         -- Extract superclass names from ConstraintExprs         superNames = map extractConstraintName superClasses-        +         -- Build method list with types-        methodsWithTypes = map extractMethodWithType methods-        +        methodsWithTypes = map (extractMethodWithType aliasEnv) methods+         -- Create ClassInfo         -- Note: Use qualified name to avoid ambiguity with ClassDecl.classMethods         classInfo = Types.ClassInfo           { Types.classSupers = superNames-          , Types.classParam = tyVar+          , Types.classParams = tyVars           , Types.classMethods = methodsWithTypes           }-        +         -- Register class         classEnv' = addClass className classInfo classEnv-        +         -- Register each class method to type environment-        typeEnv' = foldl (registerClassMethod tyVar className) typeEnv methods-    +        typeEnv' = foldl (registerClassMethod aliasEnv tyVars className) typeEnv methods+     return result { ebrClassEnv = classEnv', ebrTypeEnv = typeEnv' }-  -  ClassDeclExpr _ -> -    -- Unsupported class declaration format (multiple type parameters, etc.)++  ClassDeclExpr _ ->+    -- Class with no type parameters is rejected.     return result   -  -- 3. Instance Definitions (from InstanceDeclExpr)+  -- 3. Instance Definitions (from InstanceDeclExpr).+  -- Multi-param-friendly: instance declarations may carry one or more+  -- types (`instance Embed Integer (Frac Integer) where ...`). All of them+  -- are stored in `instTypes`; the legacy `instType` accessor reads the head.   InstanceDeclExpr (InstanceDecl context className instTypes methods) -> do     let classEnv = ebrClassEnv result         typeEnv = ebrTypeEnv result-        -        -- Get the main instance type-        mainInstType = case instTypes of++        -- Convert all instance types+        instanceTypeList = map t2t instTypes++        -- Get the primary instance type (head) for backward compatibility+        mainInstType = case instanceTypeList of           []    -> TAny-          (t:_) -> typeExprToType t-        +          (t:_) -> t+         -- Create InstanceInfo         instInfo = Types.InstanceInfo-          { Types.instContext = map constraintToInternal context+          { Types.instContext = map (constraintToInternal aliasEnv) context           , Types.instClass = className-          , Types.instType = mainInstType+          , Types.instTypes = instanceTypeList           , Types.instMethods = []  -- Methods are handled during desugaring/evaluation           }         @@ -141,10 +334,12 @@         classEnv' = addInstance className instInfo classEnv                  -- Register method type signatures for generated methods-        -- This prevents "Unbound variable" warnings during type inference-        -- Pass the instance context (constraints) to include in method types-        typeEnv' = registerInstanceMethods className mainInstType (map constraintToInternal context) methods classEnv' typeEnv-    +        -- This prevents "Unbound variable" warnings during type inference.+        -- Pass the full instance type list so the registered names match the+        -- ones Desugar emits (e.g. `embedMathValueMathValueEmbed`,+        -- `embedMathValueMathValue` for `instance Embed MathValue MathValue`).+        typeEnv' = registerInstanceMethods className mainInstType instanceTypeList (map (constraintToInternal aliasEnv) context) methods classEnv' typeEnv+     return result { ebrClassEnv = classEnv', ebrTypeEnv = typeEnv' }      -- 4. Type Signature Collection (from Define, DefineWithType)@@ -158,15 +353,16 @@         -- Create Var with index structure (content is Just Var, so map to Nothing)         var = Var name (map (fmap (const Nothing)) indexTypes)         params = typedVarParams typedVar-        retType = typeExprToType (typedVarRetType typedVar)-        paramTypes = map typedParamToType params-        -        -- Build function type-        funType = foldr TFun retType paramTypes+        retType = t2t (typedVarRetType typedVar)+        paramTypes = map (typedParamToType aliasEnv) params         +        -- Build function type, then keep declared inductive type names concrete+        -- (e.g. `nat : Matcher Nat` stays `Matcher Nat`, not `forall a. Matcher a`).+        funType = concretizeDeclaredTypes declaredTypes (foldr TFun retType paramTypes)+         -- Convert constraints from AST to internal representation-        constraints = map constraintToInternal (typedVarConstraints typedVar)-        +        constraints = map (constraintToInternal aliasEnv) (typedVarConstraints typedVar)+         -- Generalize free type variables in the type signature         -- This handles type parameters like {a, b, c} in def compose {a, b, c} ...         freeVars = Set.toList (freeTyVars funType)@@ -174,9 +370,19 @@                  typeEnv = ebrTypeEnv result         typeEnv' = extendEnv var typeScheme typeEnv-    ++    -- Restriction on open atom sets in the declared signature: a nested+    -- Poly tower may contain at most one [..] (the runtime reshape's atom+    -- routing would otherwise be ambiguous; Types.hasAmbiguousOpenTower).+    -- Definitions reach the reshape through TypedDesugar.maybeReshape, not+    -- the IReshape inference path, so the check lives at signature+    -- collection.+    when (Types.hasAmbiguousOpenTower funType) $ throwError $ Default $+      "def " ++ name ++ ": at most one open atom set [..] may appear " +++      "in a nested Poly tower: " ++ prettyType funType+     return result { ebrTypeEnv = typeEnv' }-  +   -- 5. Pattern Inductive Declarations (from PatternInductiveDecl)   PatternInductiveDecl typeName typeParams constructors -> do     let typeParamVars = map (TVar . TyVar) typeParams@@ -188,16 +394,16 @@         patternCtorEnv = ebrPatternConstructorEnv result          -- Register each pattern constructor to pattern constructor environment-    patternCtorEnv' <- foldM (registerPatternConstructor typeName typeParams patternType) -                              patternCtorEnv +    patternCtorEnv' <- foldM (registerPatternConstructor aliasEnv typeName typeParams patternType)+                              patternCtorEnv                               constructors          return result { ebrPatternConstructorEnv = patternCtorEnv' }      -- 6. Pattern Function Declarations (from PatternFunctionDecl)   PatternFunctionDecl name typeParams params retType _body -> do-    let paramTypes = map (typeExprToType . snd) params-        retType' = typeExprToType retType+    let paramTypes = map (t2t . snd) params+        retType' = t2t retType         -- Pattern function type: arg1 -> arg2 -> ... -> retType (without Pattern wrapper)         patternFuncType = foldr TFun retType' paramTypes         @@ -210,9 +416,16 @@          return result { ebrPatternTypeEnv = patternEnv' }   +  -- Phase alpha/beta: cas-type aliases and cas-subtype edges were collected+  -- in the prepass (buildEnvironments), so nothing to do per-declaration here.+  DeclareCasType _ _ -> return result+  DeclareCasSubtype _ _ -> return result+  -- M4: cas-quotient declarations are macro-expanded away before environment+  -- building (Eval.expandCasQuotientDecls); nothing should reach here.+  DeclareCasQuotient {} -> return result+   -- Other expressions don't contribute to environment building   Define {} -> return result-  DefineWithType {} -> return result   Test {} -> return result   Execute {} -> return result   LoadFile {} -> return result  -- Should not appear after expandLoads@@ -221,25 +434,90 @@   -- 7. Symbol Declarations (from DeclareSymbol)   DeclareSymbol names mTypeExpr -> do     let ty = case mTypeExpr of-               Just texpr -> typeExprToType texpr-               Nothing    -> TInt  -- Default to Integer (MathExpr)+               Just texpr -> t2t texpr+               Nothing    -> TInt  -- Default to Integer (MathValue)         scheme = Forall [] [] ty         typeEnv = ebrTypeEnv result         -- Add each symbol to the type environment         typeEnv' = foldr (\name env -> extendEnv (stringToVar name) scheme env) typeEnv names     return result { ebrTypeEnv = typeEnv' } +  -- 8. Reduction Rule Declarations (from DeclareRule, Phase 7.4)+  -- The parser accepts the rule and we now stash the (name, level, lhs, rhs)+  -- tuple in `ebrReductionRules` for later inspection / Phase 7.5 use.+  -- Application during `casNormalize` is still pending.+  DeclareRule mname level lhs rhs ->+    return result { ebrReductionRules = ebrReductionRules result +++                                          [(mname, level, lhs, rhs)] }++  -- 8b. Ideal declarations (G3 of cas-simplification).  They desugar to+  -- ordinary definitions (idealRules.N / autoRule.N / mathNormalize),+  -- which are typed like any other define; nothing to register here.+  DeclareIdeal _ -> return result++  -- 9. Derivative Declarations (from DeclareDerivative, Phase 6.3)+  -- Stash the (name, expr) pair in `ebrDerivativeRules`. Wiring into+  -- `Differentiable Factor`'s connection rule is still pending.+  DeclareDerivative name rhs ->+    return result { ebrDerivativeRules = ebrDerivativeRules result +++                                            [(name, rhs)] }++  -- 10. Math function declarations (Phase 6.3 part 5).+  -- Register the function's type signature so the inference engine knows+  -- `f : MathValue -> MathValue` (the wrapper body that quotes the symbol).+  -- Without this, binary operations like `f 3 + f 4` infer `Any + Any` and+  -- the type-class `+` dispatch fails (returning the method-name string+  -- "plus" where a CASData was expected).+  -- The default `MathValue -> MathValue` may be widened by a subsequent+  -- `declare apply` based on its argument count (see DeclareApply below).+  DeclareMathFunc name mTypeExpr -> do+    let ty = case mTypeExpr of+               Just texpr -> t2t texpr+               Nothing    -> TFun TMathValue TMathValue+        scheme = Forall [] [] ty+        typeEnv = ebrTypeEnv result+        typeEnv' = extendEnv (stringToVar name) scheme typeEnv+        names'   = Set.insert name (ebrMathFuncNames result)+    return result { ebrTypeEnv = typeEnv', ebrMathFuncNames = names' }++  -- 11. Math function application rules (Phase A of declare apply impl).+  -- Requires a prior `declare mathfunc <name>` so the function's type and+  -- intent are known. The actual implementation is emitted by Desugar as a+  -- plain `def` that overrides the mathfunc wrapper.+  -- Updates the registered type to `MathValue -> MathValue -> ... -> MathValue`+  -- (one MathValue per arg + result), unless `declare mathfunc` had an+  -- explicit type annotation that already matches arity.+  DeclareApply name args _body -> do+    if Set.member name (ebrMathFuncNames result)+      then do+        let arity = length args+            -- Build MathValue -> MathValue -> ... -> MathValue (n+1 MathValues)+            mathFunTy 0 = TMathValue+            mathFunTy n = TFun TMathValue (mathFunTy (n - 1))+            ty' = mathFunTy arity+            scheme' = Forall [] [] ty'+            typeEnv' = extendEnv (stringToVar name) scheme' (ebrTypeEnv result)+        return result { ebrTypeEnv = typeEnv' }+      else throwError $ Default $+        "declare apply " ++ name ++ ": no prior `declare mathfunc " ++ name ++ "`. " +++        "Add `declare mathfunc " ++ name ++ "` before this `declare apply` declaration."++  where+    -- typeExprToType + cas-type alias expansion (Phase alpha).+    t2t :: TypeExpr -> Type+    t2t = Types.expandTypeAliases aliasEnv . typeExprToType+ -------------------------------------------------------------------------------- -- Helper Functions --------------------------------------------------------------------------------  -- | Register a single data constructor-registerConstructor :: String -> [String] -> Type -                    -> (TypeEnv, ConstructorEnv) -> InductiveConstructor +registerConstructor :: HashMap.HashMap String Type -> String -> [String] -> Type+                    -> (TypeEnv, ConstructorEnv) -> InductiveConstructor                     -> EvalM (TypeEnv, ConstructorEnv)-registerConstructor typeName typeParams resultType (typeEnv, ctorEnv) +registerConstructor aliasEnv typeName typeParams resultType (typeEnv, ctorEnv)                     (InductiveConstructor ctorName argTypeExprs) = do-  let argTypes = map typeExprToType argTypeExprs+  let argTypes = map (Types.expandTypeAliases aliasEnv . typeExprToType) argTypeExprs              -- Constructor type: argTypes -> resultType       constructorType = foldr TFun resultType argTypes@@ -261,44 +539,53 @@      return (typeEnv', ctorEnv') --- | Register a class method to the type environment-registerClassMethod :: TyVar -> String -> TypeEnv -> ClassMethod -> TypeEnv-registerClassMethod tyVar className typeEnv (ClassMethod methName params retType _defaultImpl) =-  let paramTypes = map typedParamToType params-      methodType = foldr TFun (typeExprToType retType) paramTypes-      -      -- Method has constrained type: ClassName a => methodType-      constraint = Types.Constraint className (TVar tyVar)-      typeScheme = Types.Forall [tyVar] [constraint] methodType+-- | Register a class method to the type environment.+-- The constraint carries ALL class type parameters (multi-param-friendly).+-- For `class Coerce a b where coerce (x: a) : b`, the method is registered+-- with `forall a b. Coerce a b => a -> b`.+registerClassMethod :: HashMap.HashMap String Type -> [TyVar] -> String -> TypeEnv -> ClassMethod -> TypeEnv+registerClassMethod aliasEnv tyVars className typeEnv (ClassMethod methName params retType _defaultImpl) =+  let paramTypes = map (typedParamToType aliasEnv) params+      methodType = foldr TFun (Types.expandTypeAliases aliasEnv (typeExprToType retType)) paramTypes+      -- Constraint with all class type params (single-param classes still+      -- produce a singleton list).+      constraint = Types.Constraint className (map TVar tyVars)+      typeScheme = Types.Forall tyVars [constraint] methodType   in     extendEnv (stringToVar methName) typeScheme typeEnv  -- | Register type signatures for instance methods (generated during desugaring)--- This prevents "Unbound variable" warnings during type inference-registerInstanceMethods :: String -> Type -> [Constraint] -> [InstanceMethod] -> ClassEnv -> TypeEnv -> TypeEnv-registerInstanceMethods className instType instConstraints methods classEnv typeEnv =+-- This prevents "Unbound variable" warnings during type inference.+--+-- Names must match Desugar's `desugarInstanceMethod` / `makeDictDef`, which+-- concatenate the type-constructor name of EVERY instance type (multi-param+-- friendly): e.g. `instance Embed MathValue MathValue` →+-- `embedMathValueMathValueEmbed` (method) and `embedMathValueMathValue`+-- (dictionary).+registerInstanceMethods :: String -> Type -> [Type] -> [Constraint] -> [InstanceMethod] -> ClassEnv -> TypeEnv -> TypeEnv+registerInstanceMethods className instType instTypeList instConstraints methods classEnv typeEnv =   case lookupClass className classEnv of     Nothing -> typeEnv  -- Class not found, skip-    Just classInfo -> +    Just classInfo ->       -- Register each instance method-      let typeEnv' = foldr (registerInstanceMethod className instType instConstraints classInfo) typeEnv methods-      +      let typeEnv' = foldr (registerInstanceMethod className instType instTypeList instConstraints classInfo) typeEnv methods+           -- Also register the dictionary itself-          -- e.g., eqCollection : {Eq a} Hash String ([a] -> [a] -> Bool)-          typeName' = Types.typeConstructorName instType-          dictName = lowerFirst className ++ typeName'-          +          -- e.g., embedMathValueMathValue : Hash String (MathValue -> MathValue)+          instTypeName = concatMap Types.typeToName instTypeList+          dictName = lowerFirst className ++ instTypeName+           -- Build dictionary type: Hash String (method type)           -- All methods should have the same general shape, so we use the first one           dictValueType = case methods of             [] -> TAny-            _ -> case lookup (instanceMethodName (head methods)) (Types.classMethods classInfo) of+            (m:_) -> case lookup (instanceMethodName m) (Types.classMethods classInfo) of               Nothing -> TAny               Just methodType ->                 let tyVar = Types.classParam classInfo                     substitutedType = substituteTypeVar tyVar instType methodType                 in substitutedType-          +           dictType = THash TString dictValueType           freeVars = Set.toList (freeTyVars dictType)           dictScheme = Types.Forall freeVars instConstraints dictType@@ -307,26 +594,26 @@   where     instanceMethodName :: InstanceMethod -> String     instanceMethodName (InstanceMethod name _ _) = name-    -    registerInstanceMethod :: String -> Type -> [Constraint] -> Types.ClassInfo -> InstanceMethod -> TypeEnv -> TypeEnv-    registerInstanceMethod clsName instTy constraints classInfo (InstanceMethod methName _params _body) env =++    registerInstanceMethod :: String -> Type -> [Type] -> [Constraint] -> Types.ClassInfo -> InstanceMethod -> TypeEnv -> TypeEnv+    registerInstanceMethod clsName instTy instTyList constraints classInfo (InstanceMethod methName _params _body) env =       -- Find the method in the class definition       case lookup methName (Types.classMethods classInfo) of         Nothing -> env  -- Method not in class definition, skip-        Just methodType -> -          -- Substitute type variable with instance type+        Just methodType ->+          -- Substitute type variable with instance type (use first type for the method body type)           let tyVar = Types.classParam classInfo               substitutedType = substituteTypeVar tyVar instTy methodType-              -              -- Generate method name using type constructor name only (no type parameters)-              -- e.g., "eqCollectionEq" not "eqCollectionaEq"-              typeName' = Types.typeConstructorName instTy++              -- Generate method name from ALL instance types (matching Desugar)+              -- e.g., "embedMathValueMathValueEmbed" for instance Embed MathValue MathValue+              instTypeName = concatMap Types.typeToName instTyList               sanitizedName = sanitizeMethodName methName-              generatedMethodName = lowerFirst clsName ++ typeName' ++ capitalizeFirst sanitizedName-              +              generatedMethodName = lowerFirst clsName ++ instTypeName ++ capitalizeFirst sanitizedName+               -- Extract free type variables from the substituted type               freeVars = Set.toList (freeTyVars substitutedType)-              +               -- Create type scheme with constraints from the instance context               -- e.g., {Eq a} [a] -> [a] -> Bool for instance {Eq a} Eq [a]               typeScheme = Types.Forall freeVars constraints substitutedType@@ -335,55 +622,34 @@          -- Substitute type variable with concrete type in a type expression     substituteTypeVar :: TyVar -> Type -> Type -> Type-    substituteTypeVar oldVar newType = go-      where-        go TInt = TInt-        go TFloat = TFloat-        go TBool = TBool-        go TChar = TChar-        go TString = TString-        go (TVar v) | v == oldVar = newType-                    | otherwise = TVar v-        go (TTuple ts) = TTuple (map go ts)-        go (TCollection t) = TCollection (go t)-        go (TInductive name ts) = TInductive name (map go ts)-        go (TTensor t) = TTensor (go t)-        go (THash k v) = THash (go k) (go v)-        go (TMatcher t) = TMatcher (go t)-        go (TFun t1 t2) = TFun (go t1) (go t2)-        go (TIO t) = TIO (go t)-        go (TIORef t) = TIORef (go t)-        go TAny = TAny---- | Extract method name from ClassMethod-extractMethodName :: ClassMethod -> String-extractMethodName (ClassMethod name _ _ _) = name+    substituteTypeVar = Types.substTyVar  -- | Extract method name and type from ClassMethod-extractMethodWithType :: ClassMethod -> (String, Type)-extractMethodWithType (ClassMethod name params retType _) =-  let paramTypes = map typedParamToType params-      methodType = foldr TFun (typeExprToType retType) paramTypes+extractMethodWithType :: HashMap.HashMap String Type -> ClassMethod -> (String, Type)+extractMethodWithType aliasEnv (ClassMethod name params retType _) =+  let paramTypes = map (typedParamToType aliasEnv) params+      methodType = foldr TFun (Types.expandTypeAliases aliasEnv (typeExprToType retType)) paramTypes   in (name, methodType)  -- | Extract class name from ConstraintExpr extractConstraintName :: ConstraintExpr -> String extractConstraintName (ConstraintExpr clsName _) = clsName --- | Convert ConstraintExpr to internal Constraint-constraintToInternal :: ConstraintExpr -> Types.Constraint-constraintToInternal (ConstraintExpr clsName tyExprs) =-  Types.Constraint clsName (case tyExprs of -    [] -> TAny-    (t:_) -> typeExprToType t)+-- | Convert ConstraintExpr to internal Constraint.+-- Multi-param classes (e.g. `Coerce a b`) carry all class type parameters.+constraintToInternal :: HashMap.HashMap String Type -> ConstraintExpr -> Types.Constraint+constraintToInternal aliasEnv (ConstraintExpr clsName tyExprs) =+  Types.Constraint clsName (case tyExprs of+    [] -> [TAny]+    _  -> map (Types.expandTypeAliases aliasEnv . typeExprToType) tyExprs)  -- | Register a single pattern constructor-registerPatternConstructor :: String -> [String] -> Type -                           -> PatternConstructorEnv -> PatternConstructor +registerPatternConstructor :: HashMap.HashMap String Type -> String -> [String] -> Type+                           -> PatternConstructorEnv -> PatternConstructor                            -> EvalM PatternConstructorEnv-registerPatternConstructor _typeName typeParams resultType patternCtorEnv +registerPatternConstructor aliasEnv _typeName typeParams resultType patternCtorEnv                           (PatternConstructor ctorName argTypeExprs) = do-  let argTypes = map typeExprToType argTypeExprs+  let argTypes = map (Types.expandTypeAliases aliasEnv . typeExprToType) argTypeExprs              -- Pattern constructor type: arg1 -> arg2 -> ... -> resultType (without Pattern wrapper)       patternCtorType = foldr TFun resultType argTypes@@ -397,12 +663,15 @@      return patternCtorEnv' --- | Convert TypedParam to Type-typedParamToType :: TypedParam -> Type-typedParamToType (TPVar _ ty) = typeExprToType ty-typedParamToType (TPInvertedVar _ ty) = typeExprToType ty-typedParamToType (TPTuple elems) = TTuple (map typedParamToType elems)-typedParamToType (TPWildcard ty) = typeExprToType ty-typedParamToType (TPUntypedVar _) = TVar (TyVar "a")  -- Will be inferred-typedParamToType TPUntypedWildcard = TVar (TyVar "a")  -- Will be inferred+-- | Convert TypedParam to Type (cas-type aliases expanded)+typedParamToType :: HashMap.HashMap String Type -> TypedParam -> Type+typedParamToType aliasEnv = go+  where+    go (TPVar _ ty) = t2t ty+    go (TPInvertedVar _ ty) = t2t ty+    go (TPTuple elems) = TTuple (map go elems)+    go (TPWildcard ty) = t2t ty+    go (TPUntypedVar _) = TVar (TyVar "a")  -- Will be inferred+    go TPUntypedWildcard = TVar (TyVar "a")  -- Will be inferred+    t2t = Types.expandTypeAliases aliasEnv . typeExprToType 
hs-src/Language/Egison/Eval.hs view
@@ -9,12 +9,10 @@   2. expandLoads (File loading with caching)   3. Environment Building Phase (Collect data constructors, type classes, instances, type signatures)   4. Desugar (Syntactic desugaring)-  5. Type Inference Phase (Constraint generation, unification, type class constraint processing)-  6. Type Check Phase (Verify type annotations, check type class constraints)-  7. TypedTopExpr (Typed AST)-  8. TypedDesugar (Type-driven transformations: type class expansion, tensorMap insertion)-  9. TITopExpr (Evaluatable typed IR with type info preserved)- 10. Evaluation (Pattern matching execution, expression evaluation, IO actions)+  5-6. Type Inference Phase (Constraint generation, unification, TIExpr generation)+  7. TypedDesugar (Type-driven transformations: tensorMap insertion, type class expansion)+  8. Definition Binding (Recursive binding of all definitions)+  9. Evaluation (Pattern matching execution, expression evaluation, IO actions) -}  module Language.Egison.Eval@@ -38,7 +36,8 @@   ) where  import           Control.Monad              (foldM, forM_, when)-import           Data.List                  (intercalate, partition)+import           Data.IORef                 (newIORef)+import           Data.List                  (intercalate) import           Control.Monad.Except       (throwError, catchError) import           Control.Monad.Reader       (ask, asks) import           Control.Monad.State@@ -48,7 +47,6 @@ import           Language.Egison.CmdOptions import           Language.Egison.Core import           Language.Egison.Data-import           Language.Egison.Data.Utils     (newEvaluatedObjectRef) import           Language.Egison.Desugar (desugarExpr, desugarTopExpr, desugarTopExprs) import           Language.Egison.EnvBuilder (buildEnvironments, EnvBuildResult(..)) import           Language.Egison.EvalState  (MonadEval (..), ConstructorEnv, PatternConstructorEnv)@@ -56,16 +54,17 @@ import           Language.Egison.MathOutput (prettyMath) import           Language.Egison.Parser import qualified Language.Egison.Type.Types as Types-import           Language.Egison.Type.Infer (inferITopExpr, runInferWithWarningsAndState, InferState(..), initialInferStateWithConfig, permissiveInferConfig, defaultInferConfig)+import           Language.Egison.Type.Infer (inferITopExpr, runInferWithWarningsAndState, InferState(..), initialInferStateWithConfig, permissiveInferConfig, defaultInferConfig, cfgMatcherConsistencyWarnings) import           Language.Egison.Type.Env (TypeEnv, ClassEnv, PatternTypeEnv, extendEnvMany, envToList, classEnvToList, lookupInstances, patternEnvToList, mergeClassEnv, extendPatternEnv) import           Language.Egison.Type.TypeClassExpand () import           Language.Egison.Type.TypedDesugar (desugarTypedTopExprT_TensorMapOnly, desugarTypedTopExprT_TypeClassOnly)-import           Language.Egison.Type.Error (formatTypeError, formatTypeWarning)+import           Language.Egison.Type.Error (TypeError, formatTypeError, formatTypeWarning) import           Language.Egison.Type.Check (builtinEnv) import           Language.Egison.Type.Pretty (prettyTypeScheme, prettyType) import           Language.Egison.Pretty (prettyStr) import           Language.Egison.EvalState (ConstructorInfo(..)) import qualified Data.HashMap.Strict as HashMap+import qualified Data.Set as Set   -- | Evaluate an Egison expression.@@ -75,13 +74,8 @@ -------------------------------------------------------------------------------- -- Phase 1: expandLoads - File Loading with Caching ----------------------------------------------------------------------------------- Recursively expand all Load/LoadFile statements into a flat list of TopExprs.--- This phase handles file reading and prevents duplicate loading through caching.--- After this phase, all source code is loaded and ready for environment building.  -- | Expand all Load/LoadFile statements recursively into a flat list of TopExprs.--- Files are loaded recursively and deduplicated (same file loaded multiple times--- will only appear once in the final list). expandLoads :: [TopExpr] -> EvalM [TopExpr] expandLoads [] = return [] expandLoads (expr:rest) = case expr of@@ -104,279 +98,377 @@ --------------------------------------------------------------------------------  -- | Evaluate an Egison top expression.--- Implements the complete processing flow:---   expandLoads → Environment Building → Desugar → Type Inference/Check → ---   TypedDesugar → Evaluation evalTopExpr :: Env -> TopExpr -> EvalM (Maybe EgisonValue, Env) evalTopExpr env topExpr = do-  -- Phase 1: Expand all Load/LoadFile recursively   expanded <- expandLoads [topExpr]-  -- Phase 2-10: Process all expanded expressions through remaining pipeline   evalExpandedTopExprsTyped env expanded  -- | Evaluate expanded top expressions using typed pipeline--- TODO: Implement type environment accumulation for proper type checking evalExpandedTopExprsTyped :: Env -> [TopExpr] -> EvalM (Maybe EgisonValue, Env) evalExpandedTopExprsTyped env exprs = evalExpandedTopExprsTyped' env exprs False True  ----------------------------------------------------------------------------------- Phase 2-10: Environment Building → Desugar → Type Inference/Check → ---             TypedDesugar → Evaluation+-- Pipeline Accumulator -------------------------------------------------------------------------------- +-- | Accumulator used during per-expression fold in phases 3-8.+-- Separates value bindings, pattern function bindings, non-definition expressions,+-- and optional dump lists for --dump-typed / --dump-ti / --dump-tc flags.+data PipelineAccum = PipelineAccum+  { accumBindings       :: [(Var, IExpr)]+  , accumPatFuncBindings :: [(String, IExpr)]+  , accumNonDefExprs    :: [(ITopExpr, Bool)]+  , accumTypedExprs     :: [Maybe TITopExpr]+  , accumTiExprs        :: [Maybe TITopExpr]+  , accumTcExprs        :: [Maybe TITopExpr]+  }++emptyAccum :: PipelineAccum+emptyAccum = PipelineAccum [] [] [] [] [] []++-- | Classify an ITopExpr into one of the accumulator bins.+classifyITopExpr :: ITopExpr -> Bool -> PipelineAccum -> PipelineAccum+classifyITopExpr iExpr printValues acc = case iExpr of+  IDefine name expr ->+    acc { accumBindings = accumBindings acc ++ [(name, expr)] }+  IDefineMany defs ->+    acc { accumBindings = accumBindings acc ++ defs }+  IPatternFunctionDecl name _tyVars params _retType body ->+    let paramNames = map fst params+        patternFuncExpr = IPatternFuncExpr paramNames body+    in acc { accumPatFuncBindings = accumPatFuncBindings acc ++ [(name, patternFuncExpr)] }+  _ ->+    acc { accumNonDefExprs = accumNonDefExprs acc ++ [(iExpr, printValues)] }++--------------------------------------------------------------------------------+-- Phase 2-9: Environment Building → Desugar → Type Inference →+--            TypedDesugar → Binding → Evaluation+--------------------------------------------------------------------------------+ -- | Evaluate expanded top expressions using the typed pipeline with optional printing.--- This function implements phases 2-10 of the processing flow. evalExpandedTopExprsTyped' :: Env -> [TopExpr] -> Bool -> Bool -> EvalM (Maybe EgisonValue, Env) evalExpandedTopExprsTyped' env exprs printValues shouldDumpTyped = do   opts <- ask-  -  ---------------------------------------------------------------------------------  -- Phase 2: Environment Building Phase (完全に独立したフェーズ)-  ---------------------------------------------------------------------------------  -- Collect ALL environment information BEFORE type inference begins:-  --   1. Data constructor definitions (from InductiveDecl)-  --   2. Type class definitions (from ClassDeclExpr)-  --   3. Instance definitions (from InstanceDeclExpr)-  --   4. Type signatures (from DefineWithType)-  -  -- Get existing environments (may contain previously loaded libraries)++  -- M4 (quotient mechanism, design/type-cas-quotient.md): macro-expand+  -- `declare cas-quotient` into ordinary defs/instances/assertions BEFORE+  -- environment building, so the generated instances and signatures flow+  -- through the normal prepass.+  exprs' <- expandCasQuotientDecls exprs++  -- Phase 2: Environment Building+  buildAndMergeEnvironments exprs' opts++  -- Pre-bind declared symbols. `declare symbol K` (uppercase) would+  -- otherwise fall through to the InductiveData fallback in IVarExpr at+  -- runtime, so any def whose body mentions `K` (e.g. `def W := 1/(1-K*r^2)`)+  -- would have to *capture* an env with K bound — but the IDeclareSymbol+  -- top expr is processed in Phase 9 (after recursiveBindAll), so without+  -- this pre-binding the def closure captures an env where K is missing.+  -- Binding here makes the symbol visible to subsequent defs and to the+  -- IDeclareSymbol pass itself (which is then a no-op).+  envWithSymbols <- preBindDeclaredSymbols env exprs'++  let permissive = not (optTypeCheckStrict opts)++  -- Phases 3-8: Desugar, type-infer, typed-desugar each expression.+  -- The definition names of the whole batch let the inferencer tell a+  -- forward reference (defined later in this load unit) apart from a+  -- genuinely unknown name when a variable is unbound.+  let batchDefNames = Set.fromList+        [ n | e <- exprs'+            , Just n <- [case e of+                           Define (VarWithIndices n' _) _ -> Just n'+                           DefineWithType tv _            -> Just (typedVarName tv)+                           _                              -> Nothing] ]+  accum <- foldM (processOneExpr opts permissive printValues batchDefNames) emptyAccum exprs'++  -- Dump typed ASTs before evaluation+  when (optDumpTyped opts && shouldDumpTyped) $+    dumpPhaseExprs "Typed AST (Phase 5-6: Type Inference)" "End of Typed AST" (accumTypedExprs accum)+  when (optDumpTi opts && shouldDumpTyped) $+    dumpPhaseExprs "Typed AST after TensorMap Insertion (Phase 7a)" "End of TensorMap Insertion AST" (accumTiExprs accum)+  when (optDumpTc opts && shouldDumpTyped) $+    dumpPhaseExprs "Typed AST after Type Class Expansion (Phase 7b)" "End of Type Class Expansion AST" (accumTcExprs accum)++  -- Phase 8: Bind all definitions together (supports mutual recursion)+  envWithPatFuncs <- recursiveBindAll envWithSymbols (accumBindings accum) (accumPatFuncBindings accum)++  -- Phase 9: Evaluate non-definition expressions in order.+  -- We catch each expression's error so that subsequent expressions still+  -- run (so a single failed `assertEqual` doesn't hide errors in later+  -- assertions). But we COLLECT the errors and re-throw at the end so the+  -- outer EvalM resolves to `Left` — required for `cabal test`'s+  -- `assertEvalM` to report the test as failed.+  (lastVal, finalEnv, collectedErrs) <-+    foldM (\(lastVal, currentEnv, errsAcc) (iExpr, shouldPrint) -> do+      evalResult <- catchError+        (Right <$> evalTopExpr' currentEnv iExpr)+        (\err -> do+          liftIO $ hPutStrLn stderr $ "Evaluation error: " ++ show err+          return $ Left err)++      case evalResult of+        Left err -> return (lastVal, currentEnv, err : errsAcc)+        Right (mVal, env'') -> do+          when shouldPrint $ case mVal of+            Nothing -> return ()+            Just val -> valueToStr val >>= liftIO . putStrLn+          return (mVal, env'', errsAcc)+    ) (Nothing, envWithPatFuncs, []) (accumNonDefExprs accum)++  -- If any expression produced an error, surface the FIRST one so the+  -- outer EvalM is `Left`. The full set has already been streamed to+  -- stderr above. Re-throw only when we collected something to avoid+  -- spurious failures on success paths.+  case reverse collectedErrs of+    []      -> return ()+    (err:_) -> throwError err++  return (lastVal, finalEnv)++--------------------------------------------------------------------------------+-- M4: cas-quotient macro expansion (design/type-cas-quotient.md q1-q4)+--------------------------------------------------------------------------------++-- | Expand `declare cas-quotient Q := Base by reduce` into ordinary+-- definitions, instances, and congruence-law assertions:+--+--   def reduceQ := <reduce>                      (user AST, spliced directly)+--   def projQ (x : MathValue) : Q := casQuotientCast (reduceQ x)+--   def reprQ (v : Q) : MathValue := casQuotientCast v+--   instance Eq/AddSemigroup/../Ring Q           (homomorphic ops:+--                                                 projQ (reprQ a ∘' reprQ b))+--   assertEqual ... (q4: idempotence + congruence over a sample battery)+--+-- Nominal typing (q1) comes from registering Q in the cas-type alias+-- environment as Q -> TInductive Q [], so every annotation seam maps the+-- bare name to an opaque type that unifies only with itself and joins no+-- subtype order (D4: quotients live outside the tower; `declare+-- cas-subtype` rejects them as non-CAS types).+--+-- Notes: crossing is explicit (projQ / reprQ). The homomorphic delegation+-- is sound because reduce is a ring-homomorphism kernel projection (checked+-- by the generated congruence assertions); non-homomorphic operations+-- (inv, gcd, comparisons) must be defined on Q directly (pattern 2).+expandCasQuotientDecls :: [TopExpr] -> EvalM [TopExpr]+expandCasQuotientDecls exprs = concat <$> mapM expand exprs+  where+    expand (DeclareCasQuotient name _baseTE reduceExpr) = do+      aliases <- getCasTypeAliasEnv+      ctorEnv <- getConstructorEnv+      when (Set.member name Types.reservedCasTypeNames) $ throwError $ Default $+        "declare cas-quotient: name clashes with a builtin type: " ++ name+      when (HashMap.member name aliases) $ throwError $ Default $+        "declare cas-quotient: name is already declared (cas-type alias or quotient): " ++ name+      when (any ((== name) . ctorTypeName) (HashMap.elems ctorEnv)) $+        throwError $ Default $+          "declare cas-quotient: name clashes with an inductive type: " ++ name+      -- q1: nominal registration through the alias environment+      setCasTypeAliasEnv (HashMap.insert name (Types.TInductive name []) aliases)+      generated <- readTopExprs (casQuotientTemplate name)+      return (Define (VarWithIndices ("reduce" ++ name) []) reduceExpr : generated)+    expand e = return [e]++-- | The generated program for one quotient declaration (q2/q3/q4).+-- Kept as concrete source text and re-parsed: every piece is ordinary+-- Egison, which keeps the mechanism a plain macro.+casQuotientTemplate :: String -> String+casQuotientTemplate q = unlines+  [ "def proj" ++ q ++ " (x : MathValue) : " ++ q ++ " := casQuotientCast (" ++ red ++ " x)"+  , "def repr" ++ q ++ " (v : " ++ q ++ ") : MathValue := casQuotientCast v"+  , "instance Eq " ++ q ++ " where"+  , "  (==) a b := (" ++ red ++ " ((repr" ++ q ++ " a) -' (repr" ++ q ++ " b))) = 0"+  , "  (/=) a b := not ((" ++ red ++ " ((repr" ++ q ++ " a) -' (repr" ++ q ++ " b))) = 0)"+  , "instance AddSemigroup " ++ q ++ " where"+  , "  (+) a b := proj" ++ q ++ " ((repr" ++ q ++ " a) +' (repr" ++ q ++ " b))"+  , "instance AddMonoid " ++ q ++ " where"+  , "  zero := proj" ++ q ++ " 0"+  , "instance AddGroup " ++ q ++ " where"+  , "  neg a := proj" ++ q ++ " (0 -' (repr" ++ q ++ " a))"+  , "instance MulSemigroup " ++ q ++ " where"+  , "  (*) a b := proj" ++ q ++ " ((repr" ++ q ++ " a) *' (repr" ++ q ++ " b))"+  , "instance MulMonoid " ++ q ++ " where"+  , "  one := proj" ++ q ++ " 1"+  , "instance Ring " ++ q+  , "assertEqual \"cas-quotient " ++ q ++ ": reduce is idempotent (sample battery)\""+  , "  (map (\\s -> " ++ red ++ " (" ++ red ++ " s)) " ++ battery ++ ")"+  , "  (map (\\s -> " ++ red ++ " s) " ++ battery ++ ")"+  , "assertEqual \"cas-quotient " ++ q ++ ": reduce is a congruence for +' (sample battery)\""+  , "  (map (\\p -> " ++ red ++ " ((fst p) +' (snd p))) " ++ pairs ++ ")"+  , "  (map (\\p -> " ++ red ++ " ((" ++ red ++ " (fst p)) +' (" ++ red ++ " (snd p)))) " ++ pairs ++ ")"+  , "assertEqual \"cas-quotient " ++ q ++ ": reduce is a congruence for *' (sample battery)\""+  , "  (map (\\p -> " ++ red ++ " ((fst p) *' (snd p))) " ++ pairs ++ ")"+  , "  (map (\\p -> " ++ red ++ " ((" ++ red ++ " (fst p)) *' (" ++ red ++ " (snd p)))) " ++ pairs ++ ")"+  ]+  where+    red = "reduce" ++ q+    battery = "[0, 1, -1, 2, 5, 12]"+    pairs = "[(0, 1), (1, 2), (-1, 5), (2, 12), (5, -1), (12, 7)]"++--------------------------------------------------------------------------------+-- Phase 2: Environment Building & Merging+--------------------------------------------------------------------------------++buildAndMergeEnvironments :: [TopExpr] -> EgisonOpts -> EvalM ()+buildAndMergeEnvironments exprs opts = do   currentTypeEnv <- getTypeEnv   currentClassEnv <- getClassEnv   currentPatternEnv <- getPatternEnv+  currentPatternFuncEnv <- getPatternFuncEnv -  -- Build environments from current expressions   envResult <- buildEnvironments exprs -  -- Merge existing environments with newly built environments-  -- New definitions extend existing ones (can override)   let newTypeEnv = ebrTypeEnv envResult-      -- If currentTypeEnv is empty, use builtinEnv as base       baseTypeEnv = if null (envToList currentTypeEnv) then builtinEnv else currentTypeEnv       mergedTypeEnv = extendEnvMany (envToList newTypeEnv) baseTypeEnv       mergedClassEnv = mergeClassEnv currentClassEnv (ebrClassEnv envResult)-      -- Merge pattern environments (new definitions can override)-      -- Pattern constructors from ebrPatternConstructorEnv and pattern functions from ebrPatternTypeEnv       patternConstructorEnv = ebrPatternConstructorEnv envResult       newPatternFuncEnv = ebrPatternTypeEnv envResult-  -  -- Get current pattern function environment-  currentPatternFuncEnv <- getPatternFuncEnv-  -  let -- Merge both into a single pattern environment-      mergedPatternEnv = foldr (\(name, scheme) env -> extendPatternEnv name scheme env) -                               (foldr (\(name, scheme) env -> extendPatternEnv name scheme env)+      mergedPatternEnv = foldr (\(name, scheme) e -> extendPatternEnv name scheme e)+                               (foldr (\(name, scheme) e -> extendPatternEnv name scheme e)                                       currentPatternEnv                                       (patternEnvToList patternConstructorEnv))                                (patternEnvToList newPatternFuncEnv)-      -- Also update pattern function environment separately-      mergedPatternFuncEnv = foldr (\(name, scheme) env -> extendPatternEnv name scheme env)+      mergedPatternFuncEnv = foldr (\(name, scheme) e -> extendPatternEnv name scheme e)                                    currentPatternFuncEnv                                    (patternEnvToList newPatternFuncEnv) -  -- Update EvalState with merged environments   setTypeEnv mergedTypeEnv   setClassEnv mergedClassEnv   setPatternEnv mergedPatternEnv   setPatternFuncEnv mergedPatternFuncEnv-  -  -- Register constructors to EvalState++  -- Phase alpha (extensible CAS tower): persist `declare cas-type` aliases so+  -- Desugar (this batch) and later load batches can expand annotation types.+  prevAliases <- getCasTypeAliasEnv+  setCasTypeAliasEnv (HashMap.union (ebrCasTypeAliases envResult) prevAliases)++  -- Phase beta: persist `declare cas-subtype` edges (D1-checked in EnvBuilder).+  prevEdges <- getCasSubtypeEdges+  setCasSubtypeEdges (prevEdges ++ ebrCasSubtypeEdges envResult)++  -- Phase 7.4/7.5/6.3: surface declaration counts and names to the runtime+  -- so that inspection primitives (`numReductionRules`, `ruleNames`,+  -- `numDerivativeRules`, `derivativeNames`) can read them. The full data+  -- (LHS/RHS Exprs) is in EnvBuildResult and not yet plumbed; only counts+  -- and names propagate.+  prevR <- getReductionRulesCount+  setReductionRulesCount (prevR + length (ebrReductionRules envResult))+  prevD <- getDerivativeRulesCount+  setDerivativeRulesCount (prevD + length (ebrDerivativeRules envResult))+  prevRNames <- getReductionRuleNames+  setReductionRuleNames (prevRNames +++    [ n | (Just n, _, _, _) <- ebrReductionRules envResult ])+  prevDNames <- getDerivativeRuleNames+  setDerivativeRuleNames (prevDNames +++    [ n | (n, _) <- ebrDerivativeRules envResult ])+   forM_ (HashMap.toList (ebrConstructorEnv envResult)) $ \(ctorName, ctorInfo) ->     registerConstructor ctorName ctorInfo-  -  -- Dump environment if requested-  when (optDumpEnv opts) $ do-    dumpEnvironment mergedTypeEnv mergedClassEnv (ebrConstructorEnv envResult) ++  when (optDumpEnv opts) $+    dumpEnvironment mergedTypeEnv mergedClassEnv (ebrConstructorEnv envResult)                     (ebrPatternConstructorEnv envResult) (ebrPatternTypeEnv envResult)-  -  -- Dump desugared AST if requested+   when (optDumpDesugared opts) $ do     desugaredExprs <- desugarTopExprs exprs     dumpDesugared (map Just desugaredExprs)-  -  -- Get the environments for type inference-  -- Permissive mode allows falling back to untyped evaluation on type errors-  let permissive = not (optTypeCheckStrict opts)-  -  -- Process each expression sequentially through phases 3-8 (type inference and desugaring)-  -- Collect all definitions to bind them together later (Phase 9)-  -- Non-definition expressions (ITest, IExecute) will be evaluated in Phase 10-  -- Also collect typed ASTs if dump-typed, dump-ti, or dump-tc is enabled-  -- The accumulator separates regular value bindings from pattern function bindings so-  -- they can be placed in different environments after collection.-  ((allBindings, allPatFuncBindings, nonDefExprs), typedExprs, tiExprs, tcExprs) <- foldM (\((bindings, patFuncBindings, nonDefs), typedExprs, tiExprs, tcExprs) expr -> do-    -- Get current type and class environments from EvalState-    currentTypeEnv <- getTypeEnv-    currentClassEnv <- getClassEnv-    -    -- Phase 3-4: Desugar (TopExpr → ITopExpr)-    mITopExpr <- desugarTopExpr expr-    -    case mITopExpr of-      Nothing -> return ((bindings, patFuncBindings, nonDefs), typedExprs, tiExprs, tcExprs)  -- No desugared output-      Just iTopExpr -> do-        -- Phase 5-6: Type Inference (ITopExpr → TypedITopExpr)-        let inferConfig = if permissive then permissiveInferConfig else defaultInferConfig-        -- Get the current pattern environment from EvalState-        currentPatternEnv' <- getPatternEnv-        currentPatternFuncEnv' <- getPatternFuncEnv-        -- Add pattern function types to inferEnv so they can be referenced as variables-        let patternFuncBindings = [(stringToVar name, scheme) | (name, scheme) <- patternEnvToList currentPatternFuncEnv']-            enrichedTypeEnv = extendEnvMany patternFuncBindings currentTypeEnv-            initState = (initialInferStateWithConfig inferConfig) {-              inferEnv = enrichedTypeEnv,-              inferClassEnv = currentClassEnv,-              inferPatternEnv = currentPatternEnv',-              inferPatternFuncEnv = currentPatternFuncEnv'-            }-        (result, warnings, finalState) <- liftIO $ -          runInferWithWarningsAndState (inferITopExpr iTopExpr) initState-        -        let updatedTypeEnv = inferEnv finalState-        let updatedClassEnv = inferClassEnv finalState-        let updatedPatternEnv = inferPatternEnv finalState-        let updatedPatternFuncEnv = inferPatternFuncEnv finalState-    -        -- Print type warnings if any-        when (not (null warnings)) $ do-          liftIO $ mapM_ (hPutStrLn stderr . formatTypeWarning) warnings-        -        -- Update type, class, and pattern environments in EvalState-        setTypeEnv updatedTypeEnv-        setClassEnv updatedClassEnv-        setPatternEnv updatedPatternEnv-        setPatternFuncEnv updatedPatternFuncEnv-        -        case result of-          Left err -> do-            liftIO $ hPutStrLn stderr $ "Type error:\n" ++ formatTypeError err-            -- Fallback: Use untyped evaluation if type checking fails (permissive mode)-            -- Type errors are handled immediately, not collected-            topExpr' <- desugarTopExpr expr-            case topExpr' of-              Nothing -> return ((bindings, patFuncBindings, nonDefs), typedExprs, tiExprs, tcExprs)-              Just topExpr'' -> do-                -- Evaluate type-error expressions immediately (not collected)-                -- This is a fallback for permissive mode-                case topExpr'' of-                  IDefine name expr ->-                    return ((bindings ++ [(name, expr)], patFuncBindings, nonDefs), typedExprs, tiExprs, tcExprs)-                  IDefineMany defs ->-                    return ((bindings ++ defs, patFuncBindings, nonDefs), typedExprs, tiExprs, tcExprs)-                  IPatternFunctionDecl name _tyVars params _retType body ->-                    let paramNames = map fst params-                        patternFuncExpr = IPatternFuncExpr paramNames body-                    in return ((bindings, patFuncBindings ++ [(name, patternFuncExpr)], nonDefs), typedExprs, tiExprs, tcExprs)-                  _ ->-                    -- Non-definition: collect for later evaluation-                    return ((bindings, patFuncBindings, nonDefs ++ [(topExpr'', printValues)]), typedExprs, tiExprs, tcExprs) -          Right (Nothing, _subst) ->-            -- No code generated (e.g., load statements that are already processed)-            return ((bindings, patFuncBindings, nonDefs), typedExprs, tiExprs, tcExprs)-          -          Right (Just tiTopExpr, _subst) -> do-            -- Phase 7: inferITopExpr now returns TITopExpr directly-            -- No need for separate conversion--            -- Collect typed AST for --dump-typed (Phase 6: after type inference, before TypedDesugar)-            let typedExprs' = if optDumpTyped opts then typedExprs ++ [Just tiTopExpr] else typedExprs--            -- Phase 8a: TensorMap Insertion-            -- Insert tensorMap where needed (scalar vs tensor argument type conversion)-            mTiTopExprAfterTensorMap <- desugarTypedTopExprT_TensorMapOnly tiTopExpr--            case mTiTopExprAfterTensorMap of-              Nothing ->-                -- Load/LoadFile statements - no evaluation needed-                return ((bindings, patFuncBindings, nonDefs), typedExprs', tiExprs, tcExprs)--              Just tiTopExprAfterTensorMap -> do-                -- Collect TensorMap-inserted AST for --dump-ti (after TensorMap insertion)-                let tiExprs' = if optDumpTi opts then tiExprs ++ [Just tiTopExprAfterTensorMap] else tiExprs--                -- Phase 8b: Type Class Expansion-                -- Expand type class method calls to dictionary-based dispatch-                mTcTopExprAfterTypeClass <- desugarTypedTopExprT_TypeClassOnly tiTopExprAfterTensorMap+--------------------------------------------------------------------------------+-- Per-Expression Pipeline (Phases 3-8)+-------------------------------------------------------------------------------- -                case mTcTopExprAfterTypeClass of-                  Nothing ->-                    -- Load/LoadFile statements - no evaluation needed-                    return ((bindings, patFuncBindings, nonDefs), typedExprs', tiExprs', tcExprs)+processOneExpr :: EgisonOpts -> Bool -> Bool -> Set.Set String -> PipelineAccum -> TopExpr -> EvalM PipelineAccum+processOneExpr opts permissive printValues batchDefNames acc expr = do+  currentTypeEnv <- getTypeEnv+  currentClassEnv <- getClassEnv -                  Just tcTopExprAfterTypeClass -> do-                    -- Collect TypeClass-expanded AST for --dump-tc (after TypeClass expansion)-                    let tcExprs' = if optDumpTc opts then tcExprs ++ [Just tcTopExprAfterTypeClass] else tcExprs+  mITopExpr <- desugarTopExpr expr -                    -- Extract ITopExpr for evaluation-                    let iTopExprExpanded = stripTypeTopExpr tcTopExprAfterTypeClass+  case mITopExpr of+    Nothing -> return acc+    Just iTopExpr -> do+      -- Phase 5-6: Type Inference+      let inferConfig = (if permissive then permissiveInferConfig else defaultInferConfig)+                          { cfgMatcherConsistencyWarnings = optMatcherConsistencyWarnings opts }+      currentPatternEnv' <- getPatternEnv+      currentPatternFuncEnv' <- getPatternFuncEnv+      currentPatternFuncStructEnv' <- getPatternFuncStructEnv+      currentCasEdges <- getCasSubtypeEdges+      let patternFuncBindings = [(stringToVar name, scheme) | (name, scheme) <- patternEnvToList currentPatternFuncEnv']+          enrichedTypeEnv = extendEnvMany patternFuncBindings currentTypeEnv+          initState = (initialInferStateWithConfig inferConfig) {+            inferEnv = enrichedTypeEnv,+            inferClassEnv = currentClassEnv,+            inferPatternEnv = currentPatternEnv',+            inferPatternFuncEnv = currentPatternFuncEnv',+            inferPatternFuncStructEnv = currentPatternFuncStructEnv',+            inferCasSubtypeEdges = currentCasEdges,+            inferBatchDefNames = batchDefNames+          }+      (result, warnings, finalState) <- liftIO $+        runInferWithWarningsAndState (inferITopExpr iTopExpr) initState -                    -- Type scheme is already in the environment (added by inferITopExpr), no need to add again+      when (not (null warnings)) $+        liftIO $ mapM_ (hPutStrLn stderr . formatTypeWarning) warnings -                    -- Phase 9-10: Collect definitions and non-definitions-                    -- Definitions will be bound together using recursiveBind to support mutual recursion-                    -- Non-definitions will be evaluated sequentially after all definitions are bound-                    case iTopExprExpanded of-                      IDefine name expr ->-                        -- Collect definition for later binding-                        return ((bindings ++ [(name, expr)], patFuncBindings, nonDefs), typedExprs', tiExprs', tcExprs')-                      IDefineMany defs ->-                        -- Collect multiple definitions for later binding-                        return ((bindings ++ defs, patFuncBindings, nonDefs), typedExprs', tiExprs', tcExprs')-                      IPatternFunctionDecl name _tyVars params _retType body ->-                        -- Collect pattern function definition separately; it will be bound-                        -- into the pattern function environment (not the value environment)-                        -- via recursiveBindPatFuncs after all regular definitions are bound.-                        let paramNames = map fst params-                            patternFuncExpr = IPatternFuncExpr paramNames body-                        in return ((bindings, patFuncBindings ++ [(name, patternFuncExpr)], nonDefs), typedExprs', tiExprs', tcExprs')-                      _ ->-                        -- Non-definition expressions (ITest, IExecute)-                        -- Collect for evaluation after all definitions are bound-                        return ((bindings, patFuncBindings, nonDefs ++ [(iTopExprExpanded, printValues)]), typedExprs', tiExprs', tcExprs')-    ) (([], [], []), [], [], []) exprs+      setTypeEnv (inferEnv finalState)+      setClassEnv (inferClassEnv finalState)+      setPatternEnv (inferPatternEnv finalState)+      setPatternFuncEnv (inferPatternFuncEnv finalState)+      setPatternFuncStructEnv (inferPatternFuncStructEnv finalState) -  -- Dump typed AST BEFORE evaluation (so dumps are available even if evaluation fails)-  -- This is important for debugging - we want to see the typed AST even when there are runtime errors-  when (optDumpTyped opts && shouldDumpTyped) $ do-    dumpTyped typedExprs+      case result of+        Left err -> handleTypeError err acc expr printValues -  when (optDumpTi opts && shouldDumpTyped) $ do-    dumpTi tiExprs+        Right (Nothing, _subst) ->+          return acc -  when (optDumpTc opts && shouldDumpTyped) $ do-    dumpTc tcExprs+        Right (Just tiTopExpr, _subst) ->+          runTypedDesugaring opts acc tiTopExpr printValues -  -- Phase 9: Bind all regular value definitions and pattern function definitions-  -- together in a single step via recursiveBindAll so that every thunk is closed-  -- over a single environment that contains both regular values and pattern-  -- functions.  Regular values go into the normal env layers; pattern functions-  -- go into the separate PatFuncEnv.  This is necessary because ordinary-  -- definitions may contain matchAll expressions that invoke pattern functions.-  envWithPatFuncs <- recursiveBindAll env allBindings allPatFuncBindings+-- | Handle type error: fall back to untyped evaluation in permissive mode.+handleTypeError :: TypeError -> PipelineAccum -> TopExpr -> Bool -> EvalM PipelineAccum+handleTypeError err acc expr printValues = do+  liftIO $ hPutStrLn stderr $ "Type error:\n" ++ formatTypeError err+  topExpr' <- desugarTopExpr expr+  case topExpr' of+    Nothing      -> return acc+    Just iExpr   -> return $ classifyITopExpr iExpr printValues acc -  -- Phase 10: Evaluate non-definition expressions in order-  (lastVal, finalEnv) <- foldM (\(lastVal, currentEnv) (iExpr, shouldPrint) -> do-      evalResult <- catchError-        (Right <$> evalTopExpr' currentEnv iExpr)-        (\err -> do-          liftIO $ hPutStrLn stderr $ "Evaluation error: " ++ show err-          return $ Left err)+-- | Run TensorMap insertion and TypeClass expansion (Phase 7a-7b),+-- then classify the resulting ITopExpr.+runTypedDesugaring :: EgisonOpts -> PipelineAccum -> TITopExpr -> Bool -> EvalM PipelineAccum+runTypedDesugaring opts acc tiTopExpr printValues = do+  let acc1 = if optDumpTyped opts+             then acc { accumTypedExprs = accumTypedExprs acc ++ [Just tiTopExpr] }+             else acc -      case evalResult of-        Left _ -> return (lastVal, currentEnv)-        Right (mVal, env'') -> do-          when shouldPrint $ case mVal of-            Nothing -> return ()-            Just val -> valueToStr val >>= liftIO . putStrLn-          return (mVal, env'')-    ) (Nothing, envWithPatFuncs) nonDefExprs+  -- Phase 7a: TensorMap Insertion+  mAfterTensor <- desugarTypedTopExprT_TensorMapOnly tiTopExpr+  case mAfterTensor of+    Nothing -> return acc1+    Just afterTensor -> do+      let acc2 = if optDumpTi opts+                 then acc1 { accumTiExprs = accumTiExprs acc1 ++ [Just afterTensor] }+                 else acc1 -  return (lastVal, finalEnv)+      -- Phase 7b: Type Class Expansion+      mAfterTC <- desugarTypedTopExprT_TypeClassOnly afterTensor+      case mAfterTC of+        Nothing -> return acc2+        Just afterTC -> do+          let acc3 = if optDumpTc opts+                     then acc2 { accumTcExprs = accumTcExprs acc2 ++ [Just afterTC] }+                     else acc2+              iTopExprExpanded = stripTypeTopExpr afterTC+          return $ classifyITopExpr iTopExprExpanded printValues acc3  ----------------------------------------------------------------------------------- Phase 2 Helper: Environment Building (moved to EnvBuilder module)+-- Remaining public API ----------------------------------------------------------------------------------- | Evaluate an Egison top expression.+ evalTopExprStr :: Env -> TopExpr -> EvalM (Maybe String, Env) evalTopExprStr env topExpr = do   (val, env') <- evalTopExpr env topExpr@@ -387,27 +479,23 @@  valueToStr :: EgisonValue -> EvalM String valueToStr val = do-  mathExpr <- asks optMathExpr-  case mathExpr of+  mathValue <- asks optMathValue+  case mathValue of     Nothing   -> return (show val)     Just lang -> return (prettyMath lang val)  -- | Evaluate Egison top expressions.--- Pipeline: ExpandLoads → TypeCheck → TypedDesugar → Eval evalTopExprs :: Env -> [TopExpr] -> EvalM Env evalTopExprs env exprs = evalTopExprs' env exprs True True  -- | Evaluate Egison top expressions with control over printing and dumping. evalTopExprs' :: Env -> [TopExpr] -> Bool -> Bool -> EvalM Env evalTopExprs' env exprs printValues shouldDumpTyped = do-  -- Expand all Load/LoadFile recursively   expanded <- expandLoads exprs-  -- Evaluate using typed pipeline with printing   (_, env') <- evalExpandedTopExprsTyped' env expanded printValues shouldDumpTyped   return env'  -- | Evaluate Egison top expressions without printing.--- Pipeline: ExpandLoads → TypeCheck → TypedDesugar → Eval evalTopExprsNoPrint :: Env -> [TopExpr] -> EvalM Env evalTopExprsNoPrint env exprs = evalTopExprs' env exprs False True @@ -506,14 +594,38 @@   env' <- recursiveBindAll env bindings patFuncBindings   return (Nothing, env') evalTopExpr' env (IDeclareSymbol _names _mType) = do-  -- Symbol declarations are only used during type inference-  -- At runtime, they don't produce any value or modify the environment+  -- Symbols are pre-bound by `preBindDeclaredSymbols` before Phase 8 so+  -- that def closures (e.g. `def W := 1/(1-K*r^2)`) capture an env where+  -- the declared symbols are visible. This case is therefore a no-op.   return (Nothing, env) evalTopExpr' _env (IPatternFunctionDecl name _ _ _ _) = do-  -- Pattern function declarations are now handled via recursiveBind-  -- They should not reach here; this is a fallback   throwError $ Default $ "Pattern function " ++ name ++ " should have been converted to IPatternFuncExpr" +-- | Walk the input top exprs, collect every name from `declare symbol`,+-- and bind each to a CAS symbol value in the env. This MUST run before+-- Phase 8 (recursiveBindAll) so def closures capture an env in which the+-- declared symbols resolve correctly — particularly important for+-- uppercase names (`K`, `M`, `G`, …), which otherwise hit the+-- InductiveData fallback in `evalExprShallow env (IVarExpr name)` and+-- cause "Expected number, but found: K" at the first arithmetic use.+preBindDeclaredSymbols :: Env -> [TopExpr] -> EvalM Env+preBindDeclaredSymbols env exprs = do+  let names = concatMap collect exprs+  if null names+    then return env+    else do+      bindings <- mapM mkBinding names+      return $ extendEnv env bindings+  where+    collect :: TopExpr -> [String]+    collect (DeclareSymbol ns _) = ns+    collect _                    = []++    mkBinding :: String -> EvalM Binding+    mkBinding name = do+      ref <- liftIO $ newIORef (WHNF (Value (symbolCASData "" name)))+      return (stringToVar name, ref)+ -------------------------------------------------------------------------------- -- Environment Dumping --------------------------------------------------------------------------------@@ -559,7 +671,7 @@       else forM_ allInstances $ \(className, instInfo) -> do         let contextStr = if null (Types.instContext instInfo)               then ""-              else let showConstraint (Types.Constraint cls ty) = cls ++ " " ++ prettyType ty+              else let showConstraint (Types.Constraint cls tys) = cls ++ concatMap (\t -> " " ++ prettyType t) tys                    in intercalate ", " (map showConstraint (Types.instContext instInfo)) ++ " => "         putStrLn $ "  instance " ++ contextStr ++ className ++ " " ++ prettyType (Types.instType instInfo)     putStrLn ""@@ -615,49 +727,16 @@     putStrLn ""     putStrLn "=== End of Desugared AST ===" --- | Dump typed AST after Phase 6 (Type Inference & Check)-dumpTyped :: [Maybe TITopExpr] -> EvalM ()-dumpTyped typedExprs = do-  liftIO $ do-    putStrLn "=== Typed AST (Phase 5-6: Type Inference) ==="-    putStrLn ""-    if null typedExprs-      then putStrLn "  (none)"-      else forM_ (zip [1 :: Int ..] typedExprs) $ \(i :: Int, mExpr) ->-        case mExpr of-          Nothing -> putStrLn $ "  [" ++ show i ++ "] (skipped)"-          Just expr -> do-            putStrLn $ "  [" ++ show i ++ "] " ++ prettyStr expr-    putStrLn ""-    putStrLn "=== End of Typed AST ==="--dumpTi :: [Maybe TITopExpr] -> EvalM ()-dumpTi tiExprs = do-  liftIO $ do-    putStrLn "=== Typed AST after TensorMap Insertion (Phase 8a) ==="-    putStrLn ""-    if null tiExprs-      then putStrLn "  (none)"-      else forM_ (zip [1 :: Int ..] tiExprs) $ \(i :: Int, mExpr) ->-        case mExpr of-          Nothing -> putStrLn $ "  [" ++ show i ++ "] (skipped)"-          Just expr -> do-            putStrLn $ "  [" ++ show i ++ "] " ++ prettyStr expr-    putStrLn ""-    putStrLn "=== End of TensorMap Insertion AST ==="--dumpTc :: [Maybe TITopExpr] -> EvalM ()-dumpTc tcExprs = do-  liftIO $ do-    putStrLn "=== Typed AST after Type Class Expansion (Phase 8b) ==="-    putStrLn ""-    if null tcExprs-      then putStrLn "  (none)"-      else forM_ (zip [1 :: Int ..] tcExprs) $ \(i :: Int, mExpr) ->-        case mExpr of-          Nothing -> putStrLn $ "  [" ++ show i ++ "] (skipped)"-          Just expr -> do-            putStrLn $ "  [" ++ show i ++ "] " ++ prettyStr expr-    putStrLn ""-    putStrLn "=== End of Type Class Expansion AST ==="-+-- | Generic dump for typed AST phases (--dump-typed, --dump-ti, --dump-tc).+dumpPhaseExprs :: String -> String -> [Maybe TITopExpr] -> EvalM ()+dumpPhaseExprs header footer exprs = liftIO $ do+  putStrLn $ "=== " ++ header ++ " ==="+  putStrLn ""+  if null exprs+    then putStrLn "  (none)"+    else forM_ (zip [1 :: Int ..] exprs) $ \(i :: Int, mExpr) ->+      case mExpr of+        Nothing -> putStrLn $ "  [" ++ show i ++ "] (skipped)"+        Just expr -> putStrLn $ "  [" ++ show i ++ "] " ++ prettyStr expr+  putStrLn ""+  putStrLn $ "=== " ++ footer ++ " ==="
hs-src/Language/Egison/EvalState.hs view
@@ -25,6 +25,7 @@  import qualified Data.HashMap.Strict              as HashMap import           Data.HashMap.Strict              (HashMap)+import qualified Data.Set                         as Set  import           Language.Egison.IExpr import           Language.Egison.Type.Types       (Type, TypeScheme)@@ -57,6 +58,39 @@   , classEnv       :: ClassEnv       -- ^ Class environment (for type inference)   , patternEnv     :: PatternTypeEnv -- ^ Pattern constructor environment (for type inference)   , patternFuncEnv :: PatternTypeEnv -- ^ Pattern function environment (for disambiguation)+  , patternFuncStructEnv :: PatternTypeEnv -- ^ Pattern function structural signatures (paper PATFUN-DEF):+                                       --   for each pattern function, the scheme of+                                       --   beta_1 -> ... -> beta_k -> tau_p_body, where beta_i is the+                                       --   structural index of parameter i and tau_p_body is the body's+                                       --   structural index.  Instantiated at application sites (PAT-APP)+                                       --   to propagate the arguments' structural indices into the result.+  , reductionRulesCount  :: Int      -- ^ Phase 7.4/7.5: number of `declare rule` declarations seen+  , derivativeRulesCount :: Int      -- ^ Phase 6.3: number of `declare derivative` declarations seen+  , reductionRuleNames   :: [String] -- ^ Names of named rules ("auto" rules are excluded)+  , derivativeRuleNames  :: [String] -- ^ Names of declared derivatives (the function names)+  , autoRuleVarNames     :: [String] -- ^ Phase 7.5: full var names of auto rules (e.g. "autoRule.0").+                                       --   Accumulated as `declare rule auto` declarations are desugared,+                                       --   used to rebuild `mathNormalize` to apply each rule in sequence.+  , autoRuleTriggers     :: [Set.Set String] -- ^ Trigger-symbol set per auto rule (parallel to autoRuleVarNames).+                                       --   Each entry is the set of literal symbols/functions referenced by+                                       --   the rule's LHS. Empty set means "no specific trigger" -> always run.+                                       --   Stored as Set already (not [String]) so iterateRulesCAS can read+                                       --   it once per call without per-call Set construction.+  , derivativesDesugared :: [String] -- ^ Phase 6.3: derivative names desugared so far (in declaration order).+                                       --   Each `declare derivative` redefines `chainPartialDiff` using only+                                       --   the names *up to and including* itself, avoiding forward references+                                       --   to derivatives declared later (which would emit warnings).+  , casTypeAliasEnv :: HashMap String Type -- ^ Phase alpha (extensible CAS tower):+                                       --   `declare cas-type` transparent aliases, name -> fully+                                       --   expanded Type. Persists across load batches so aliases+                                       --   declared in a library apply to later files.+  , casSubtypeEdges :: [(Type, Type)] -- ^ Phase beta: `declare cas-subtype` edges (alias-expanded,+                                       --   declaration order, redundant edges included for node+                                       --   bookkeeping). Persists across load batches.+  , declaredSymbolOrder :: [String]  -- ^ G3 (cas-simplification): CAS symbols in `declare symbol`+                                       --   declaration order (deduplicated).  DG1: symbols declared+                                       --   earlier rank lower in the monomial order used by+                                       --   `declare ideal`, i.e. they survive in normal forms.   }  initialEvalState :: EvalState@@ -68,6 +102,17 @@   , classEnv = emptyClassEnv   , patternEnv = emptyPatternEnv   , patternFuncEnv = emptyPatternEnv+  , patternFuncStructEnv = emptyPatternEnv+  , reductionRulesCount = 0+  , derivativeRulesCount = 0+  , reductionRuleNames = []+  , derivativeRuleNames = []+  , autoRuleVarNames = []+  , autoRuleTriggers = []+  , derivativesDesugared = []+  , casTypeAliasEnv = HashMap.empty+  , casSubtypeEdges = []+  , declaredSymbolOrder = []   }  class (Applicative m, Monad m) => MonadEval m where@@ -96,17 +141,62 @@   -- Pattern function environment operations   getPatternFuncEnv :: m PatternTypeEnv   setPatternFuncEnv :: PatternTypeEnv -> m ()+  -- Pattern function structural-signature environment operations (paper PATFUN-DEF/PAT-APP)+  getPatternFuncStructEnv :: m PatternTypeEnv+  setPatternFuncStructEnv :: PatternTypeEnv -> m ()+  -- Phase 7.4/7.5: reduction-rule and derivative-rule registration counts.+  -- Counts only — full data is held by EnvBuildResult during build phase+  -- and isn't currently threaded into the runtime state.+  getReductionRulesCount :: m Int+  setReductionRulesCount :: Int -> m ()+  getDerivativeRulesCount :: m Int+  setDerivativeRulesCount :: Int -> m ()+  getReductionRuleNames :: m [String]+  setReductionRuleNames :: [String] -> m ()+  getDerivativeRuleNames :: m [String]+  setDerivativeRuleNames :: [String] -> m ()+  -- Phase 7.5: auto-rule full var names (e.g. "autoRule.0", "autoRule.1").+  -- Used to rebuild `mathNormalize` per `declare rule auto`.+  getAutoRuleVarNames :: m [String]+  setAutoRuleVarNames :: [String] -> m ()+  appendAutoRuleVarName :: String -> m ()+  -- Trigger-symbol set per auto rule, parallel to autoRuleVarNames.+  -- Read by iterateRulesCAS via getAutoRuleTriggers (returns the cached+  -- Set list directly; no per-call construction).+  getAutoRuleTriggers :: m [Set.Set String]+  appendAutoRuleTriggers :: [String] -> m ()+  -- Phase 6.3: derivative names already desugared (in declaration order).+  -- Lets each `declare derivative` see only the derivatives that come at or+  -- before it, avoiding forward references in the generated chainPartialDiff.+  getDerivativesDesugared :: m [String]+  setDerivativesDesugared :: [String] -> m ()+  appendDerivativeDesugared :: String -> m ()+  -- G3 (cas-simplification): `declare symbol` declaration order for the+  -- `declare ideal` priority list.+  getDeclaredSymbolOrder :: m [String]+  appendDeclaredSymbols :: [String] -> m ()+  -- Phase alpha (extensible CAS tower): `declare cas-type` alias environment.+  getCasTypeAliasEnv :: m (HashMap String Type)+  setCasTypeAliasEnv :: HashMap String Type -> m ()+  -- Phase beta: `declare cas-subtype` edges.+  getCasSubtypeEdges :: m [(Type, Type)]+  setCasSubtypeEdges :: [(Type, Type)] -> m ()  instance Monad m => MonadEval (StateT EvalState m) where   pushFuncName name = do     st <- get     put $ st { funcNameStack = name : funcNameStack st }     return ()-  topFuncName = head . funcNameStack <$> get+  topFuncName = do+    stack <- funcNameStack <$> get+    case stack of+      (x:_) -> return x+      []    -> error "topFuncName: function name stack is empty"   popFuncName = do     st <- get-    put $ st { funcNameStack = tail $ funcNameStack st }-    return ()+    case funcNameStack st of+      (_:rest) -> put st { funcNameStack = rest }+      []       -> error "popFuncName: function name stack is empty"   getFuncNameStack = funcNameStack <$> get      getInstanceEnv = instanceEnv <$> get@@ -164,6 +254,69 @@     st <- get     put $ st { patternFuncEnv = env } +  getPatternFuncStructEnv = patternFuncStructEnv <$> get+  setPatternFuncStructEnv env = do+    st <- get+    put $ st { patternFuncStructEnv = env }++  getReductionRulesCount = reductionRulesCount <$> get+  setReductionRulesCount n = do+    st <- get+    put $ st { reductionRulesCount = n }++  getDerivativeRulesCount = derivativeRulesCount <$> get+  setDerivativeRulesCount n = do+    st <- get+    put $ st { derivativeRulesCount = n }++  getReductionRuleNames = reductionRuleNames <$> get+  setReductionRuleNames ns = do+    st <- get+    put $ st { reductionRuleNames = ns }++  getDerivativeRuleNames = derivativeRuleNames <$> get+  setDerivativeRuleNames ns = do+    st <- get+    put $ st { derivativeRuleNames = ns }++  getAutoRuleVarNames = autoRuleVarNames <$> get+  setAutoRuleVarNames ns = do+    st <- get+    put $ st { autoRuleVarNames = ns }+  appendAutoRuleVarName n = do+    st <- get+    put $ st { autoRuleVarNames = autoRuleVarNames st ++ [n] }++  getAutoRuleTriggers = autoRuleTriggers <$> get+  appendAutoRuleTriggers ts = do+    st <- get+    put $ st { autoRuleTriggers = autoRuleTriggers st ++ [Set.fromList ts] }++  getDerivativesDesugared = derivativesDesugared <$> get+  setDerivativesDesugared ns = do+    st <- get+    put $ st { derivativesDesugared = ns }+  appendDerivativeDesugared n = do+    st <- get+    put $ st { derivativesDesugared = derivativesDesugared st ++ [n] }++  getDeclaredSymbolOrder = declaredSymbolOrder <$> get+  appendDeclaredSymbols names = do+    st <- get+    let existing = declaredSymbolOrder st+        newNames = filter (`notElem` existing) names+    put $ st { declaredSymbolOrder = existing ++ newNames }++  getCasTypeAliasEnv = casTypeAliasEnv <$> get+  setCasTypeAliasEnv env = do+    st <- get+    put $ st { casTypeAliasEnv = env }++  getCasSubtypeEdges = casSubtypeEdges <$> get+  setCasSubtypeEdges es = do+    st <- get+    put $ st { casSubtypeEdges = es }+ instance (MonadEval m) => MonadEval (ExceptT e m) where   pushFuncName name = lift $ pushFuncName name   topFuncName = lift topFuncName@@ -184,6 +337,30 @@   setPatternEnv = lift . setPatternEnv   getPatternFuncEnv = lift getPatternFuncEnv   setPatternFuncEnv = lift . setPatternFuncEnv+  getPatternFuncStructEnv = lift getPatternFuncStructEnv+  setPatternFuncStructEnv = lift . setPatternFuncStructEnv+  getReductionRulesCount = lift getReductionRulesCount+  setReductionRulesCount = lift . setReductionRulesCount+  getDerivativeRulesCount = lift getDerivativeRulesCount+  setDerivativeRulesCount = lift . setDerivativeRulesCount+  getReductionRuleNames = lift getReductionRuleNames+  setReductionRuleNames = lift . setReductionRuleNames+  getDerivativeRuleNames = lift getDerivativeRuleNames+  setDerivativeRuleNames = lift . setDerivativeRuleNames+  getAutoRuleVarNames = lift getAutoRuleVarNames+  setAutoRuleVarNames = lift . setAutoRuleVarNames+  appendAutoRuleVarName = lift . appendAutoRuleVarName+  getAutoRuleTriggers = lift getAutoRuleTriggers+  appendAutoRuleTriggers = lift . appendAutoRuleTriggers+  getDerivativesDesugared = lift getDerivativesDesugared+  setDerivativesDesugared = lift . setDerivativesDesugared+  appendDerivativeDesugared = lift . appendDerivativeDesugared+  getDeclaredSymbolOrder = lift getDeclaredSymbolOrder+  appendDeclaredSymbols = lift . appendDeclaredSymbols+  getCasTypeAliasEnv = lift getCasTypeAliasEnv+  setCasTypeAliasEnv = lift . setCasTypeAliasEnv+  getCasSubtypeEdges = lift getCasSubtypeEdges+  setCasSubtypeEdges = lift . setCasSubtypeEdges  mLabelFuncName :: MonadEval m => Maybe Var -> m a -> m a mLabelFuncName Nothing m = m
hs-src/Language/Egison/IExpr.hs view
@@ -35,6 +35,7 @@   , tipType   , stripType   , stripTypeTopExpr+  , mapTIExprChildren   , Var (..)   , stringToVar   , extractNameFromVar@@ -111,6 +112,24 @@   | IFlipIndicesExpr IExpr   | IFunctionExpr [String]   | IPatternFuncExpr [String] IPattern  -- Pattern function: parameter names and pattern body+  -- Type-driven structural reshape of a CAS value. Inserted by post-typecheck+  -- elaboration from a type annotation (`def x : T := e` becomes+  -- `IDefine x (IReshape T e)`). The Type argument is fixed at compile time;+  -- the actual structural rewrite runs at evaluation time via casReshapeAs.+  -- See design/type-cas-implementation-status.md.+  | IReshape Type IExpr+  -- Runtime-type dispatch: select an instance dictionary by inspecting the+  -- runtime CAS shape of the first argument. Emitted by TypeClassExpand when+  -- a method is called on a value whose static type is `MathValue` and no+  -- explicit `instance Class MathValue` exists. The (Type, String) list+  -- carries the candidate instance types together with their dictionary+  -- variable names; lookup happens at evaluation time. See+  -- design/runtime-type-dispatch.md.+  | IRuntimeDispatch+      String          -- class name (e.g. "Differentiable")+      String          -- method name (e.g. "partialDiff")+      [(Type, String)] -- candidates: (instance type, dict var name)+      [IExpr]         -- arguments (first one is the dispatch value)   deriving Show  type IBindingExpr = (IPrimitiveDataPattern, IExpr)@@ -210,9 +229,9 @@ -- -- Typed Internal Expressions ----------------------------------------------------------------------------------- Phase 9: TIExpr - Evaluatable Typed IR with Type Info Preserved+-- Phase 7 output: TIExpr - Typed IR with Type Info Preserved ----------------------------------------------------------------------------------- TIExpr is the result of Phase 8 (TypedDesugar) and input to Phase 10 (Evaluation).+-- TIExpr is the result of Phase 7 (TypedDesugar) and input to Phase 8-9 (Binding/Evaluation). -- It carries type information alongside the expression for: --   - Better runtime error messages with type information --   - Type-based dispatch during evaluation@@ -223,8 +242,8 @@ -- Type classes have already been resolved to dictionary passing, so no type class -- constraints are needed here. --- | Typed top-level expression (Phase 9: TITopExpr)--- Result of TypedDesugar phase, ready for evaluation.+-- | Typed top-level expression+-- Result of Phase 7 (TypedDesugar), ready for evaluation. data TITopExpr   = TIDefine TypeScheme Var TIExpr     -- ^ Typed definition with type scheme (includes type vars & constraints)   | TIDefineMany [(Var, TIExpr)]       -- ^ Multiple definitions (letrec)@@ -241,9 +260,9 @@     -- TIPattern: typed body   deriving Show --- | Typed internal expression (Phase 9: TIExpr)+-- | Typed internal expression (TIExpr) -- Each expression node carries its inferred/checked type scheme with type variables and constraints.--- TypeScheme info is preserved for Phase 8 (TypedDesugar) to perform type-driven transformations+-- TypeScheme info is preserved for Phase 7 (TypedDesugar) to perform type-driven transformations -- such as type class dictionary passing and tensorMap insertion. -- -- NEW: TIExpr is now RECURSIVE - each sub-expression is also a TIExpr,@@ -324,6 +343,14 @@      -- Function reference   | TIFunctionExpr [String]+  -- Reshape: typed mirror of IReshape. See note above.+  | TIReshape Type TIExpr+  -- Runtime-type dispatch: see `IRuntimeDispatch` in IExpr above.+  | TIRuntimeDispatch+      String           -- class name+      String           -- method name+      [(Type, String)] -- (instance type, dict var name) candidates+      [TIExpr]         -- arguments (first one is the dispatch value)   deriving Show  -- | Typed binding expression@@ -396,6 +423,9 @@   TITransposeExpr perm tensor -> ITransposeExpr (stripType perm) (stripType tensor)   TIFlipIndicesExpr tensor -> IFlipIndicesExpr (stripType tensor)   TIFunctionExpr names -> IFunctionExpr names+  TIReshape ty inner -> IReshape ty (stripType inner)+  TIRuntimeDispatch className methodName candidates args ->+    IRuntimeDispatch className methodName candidates (map stripType args)   where     stripTypeBinding :: TIBindingExpr -> IBindingExpr     stripTypeBinding (pat, expr) = (pat, stripType expr)@@ -482,6 +512,87 @@          stripTypeLoopRange :: TILoopRange -> ILoopRange     stripTypeLoopRange (TILoopRange e1 e2 pat) = ILoopRange (stripType e1) (stripType e2) (stripTypePat pat)++-- | Apply a function to all immediate TIExpr children of a TIExprNode.+-- Patterns are left untouched; use a separate pattern traversal if needed.+-- When adding a new TIExprNode constructor, add its case here to keep+-- all generic traversals (constraint resolution, etc.) working.+mapTIExprChildren :: (TIExpr -> TIExpr) -> TIExprNode -> TIExprNode+mapTIExprChildren f node = case node of+  -- Leaf nodes+  TIConstantExpr c        -> TIConstantExpr c+  TIVarExpr name          -> TIVarExpr name+  TIFunctionExpr names    -> TIFunctionExpr names++  -- Single child+  TILambdaExpr mVar ps body    -> TILambdaExpr mVar ps (f body)+  TIMemoizedLambdaExpr args body -> TIMemoizedLambdaExpr args (f body)+  TICambdaExpr var body         -> TICambdaExpr var (f body)+  TIWithSymbolsExpr syms body   -> TIWithSymbolsExpr syms (f body)+  TIQuoteExpr e                 -> TIQuoteExpr (f e)+  TIQuoteSymbolExpr e           -> TIQuoteSymbolExpr (f e)+  TITensorContractExpr e        -> TITensorContractExpr (f e)+  TIFlipIndicesExpr e           -> TIFlipIndicesExpr (f e)++  -- Two children+  TIConsExpr e1 e2               -> TIConsExpr (f e1) (f e2)+  TIJoinExpr e1 e2               -> TIJoinExpr (f e1) (f e2)+  TISeqExpr e1 e2                -> TISeqExpr (f e1) (f e2)+  TIGenerateTensorExpr fn sh     -> TIGenerateTensorExpr (f fn) (f sh)+  TITensorExpr sh el             -> TITensorExpr (f sh) (f el)+  TITensorMapExpr fn t           -> TITensorMapExpr (f fn) (f t)+  TITransposeExpr p t            -> TITransposeExpr (f p) (f t)+  TISubrefsExpr b e1 e2          -> TISubrefsExpr b (f e1) (f e2)+  TISuprefsExpr b e1 e2          -> TISuprefsExpr b (f e1) (f e2)+  TIUserrefsExpr b e1 e2         -> TIUserrefsExpr b (f e1) (f e2)++  -- Three children+  TIIfExpr c t e                    -> TIIfExpr (f c) (f t) (f e)+  TITensorMap2Expr fn t1 t2         -> TITensorMap2Expr (f fn) (f t1) (f t2)+  TITensorMap2WedgeExpr fn t1 t2    -> TITensorMap2WedgeExpr (f fn) (f t1) (f t2)++  -- List children+  TITupleExpr es           -> TITupleExpr (map f es)+  TICollectionExpr es      -> TICollectionExpr (map f es)+  TIVectorExpr es          -> TIVectorExpr (map f es)+  TIInductiveDataExpr n es -> TIInductiveDataExpr n (map f es)++  -- Function + args+  TIApplyExpr fn args      -> TIApplyExpr (f fn) (map f args)+  TIWedgeApplyExpr fn args -> TIWedgeApplyExpr (f fn) (map f args)++  -- Hash pairs+  TIHashExpr pairs -> TIHashExpr [(f k, f v) | (k, v) <- pairs]++  -- Bindings + body+  TILetExpr bs body    -> TILetExpr (mapBind f bs) (f body)+  TILetRecExpr bs body -> TILetRecExpr (mapBind f bs) (f body)+  TIDoExpr bs body     -> TIDoExpr (mapBind f bs) (f body)++  -- Pattern matching (expression children only; patterns are untouched)+  TIMatchExpr mode tgt mat cls ->+    TIMatchExpr mode (f tgt) (f mat) (mapClause f cls)+  TIMatchAllExpr mode tgt mat cls ->+    TIMatchAllExpr mode (f tgt) (f mat) (mapClause f cls)++  -- Matcher+  TIMatcherExpr pds ->+    TIMatcherExpr [(pat, f expr, mapBind f bs) | (pat, expr, bs) <- pds]++  -- Indexed+  TIIndexedExpr ov expr idxs ->+    TIIndexedExpr ov (f expr) (fmap f <$> idxs)++  -- Reshape: traverse the inner expression; type is metadata+  TIReshape ty inner ->+    TIReshape ty (f inner)++  -- Runtime dispatch: traverse arguments only; class/method/candidates are metadata+  TIRuntimeDispatch cls m cands args ->+    TIRuntimeDispatch cls m cands (map f args)+  where+    mapBind g  = map (\(p, e) -> (p, g e))+    mapClause g = map (\(p, e) -> (p, g e))  -- | Typed pattern with recursive structure (like TIExpr) data TIPattern = TIPattern
hs-src/Language/Egison/Math.hs view
@@ -5,30 +5,66 @@ Licence     : MIT  This module provides the interface of Egison's computer algebra system.+This module provides the public API for Egison's computer algebra system based on CASValue. -}  module Language.Egison.Math-  ( ScalarData (..)-  , PolyExpr (..)-  , TermExpr (..)-  , Monomial-  , SymbolExpr (..)-  , Printable (..)-  , pattern ZeroExpr-  , pattern SingleSymbol-  , pattern SingleTerm-  , mathNormalize'-  , rewriteSymbol-  , mathPlus-  , mathMult-  , mathDiv-  , mathNumerator-  , mathDenominator-  , mathNegate-  , makeApplyExpr+  ( -- * CAS Public API+    CASValue (..)+  , CASTerm (..)+  , casNormalize+  , casRewriteSymbol+  , casPlus+  , casMinus+  , casMult+  , casDivide+  , casPower+  , casNumerator+  , casDenominator+  , casNegate+  , casIsZero+  , casIsAtom+    -- ** Pretty printing+  , prettyCAS+    -- ** CAS Pattern Synonyms+  , pattern CASZero+  , pattern CASSingleSymbol+  , pattern CASSingleTerm+    -- ** CAS Pattern Matching (control-egison)+  , CASM (..)+  , CASTermM (..)+  , CASSymbolM (..)+  , casTerm'+  , casTerm'M+  , casTermM+  , casSymbol+  , casSymbolM+  , casFunc+  , casFuncM+  , casApply1+  , casApply1M+  , casApply2+  , casApply2M+  , casApply3+  , casApply3M+  , casApply4+  , casApply4M+  , casQuote+  , casNegQuote+  , casNegQuoteM+  , casQuoteFunction+  , casQuoteFunctionM+  , casEqualMonomial+  , casEqualMonomialM+  , casZero+  , casZeroM+  , casSingleTerm+  , casSingleTermM   ) where -import           Language.Egison.Math.Arith-import           Language.Egison.Math.Expr-import           Language.Egison.Math.Normalize-import           Language.Egison.Math.Rewrite+import           Language.Egison.Math.CAS hiding (SymbolExpr(..), Monomial, makeApplyExpr)+import qualified Language.Egison.Math.Rewrite as R++-- | Apply rewrite rules to a CASValue (CAS version of rewriteSymbol)+casRewriteSymbol :: CASValue -> CASValue+casRewriteSymbol = R.casRewriteSymbol
− hs-src/Language/Egison/Math/Arith.hs
@@ -1,51 +0,0 @@-{- |-Module      : Language.Egison.Math.Arith-Licence     : MIT--This module defines some basic arithmetic operations for Egison's computer-algebra system.--}--module Language.Egison.Math.Arith-  ( mathPlus-  , mathMinus-  , mathMult-  , mathDiv-  , mathPower-  , mathNumerator-  , mathDenominator-  ) where--import           Language.Egison.Math.Expr-import           Language.Egison.Math.Normalize--mathPlus :: ScalarData -> ScalarData -> ScalarData-mathPlus (Div m1 n1) (Div m2 n2) = mathNormalize' $ Div (mathPlusPoly (mathMultPoly m1 n2) (mathMultPoly m2 n1)) (mathMultPoly n1 n2)--mathPlusPoly :: PolyExpr -> PolyExpr -> PolyExpr-mathPlusPoly (Plus ts1) (Plus ts2) = Plus (ts1 ++ ts2)--mathMinus :: ScalarData -> ScalarData -> ScalarData-mathMinus s1 s2 = mathPlus s1 (mathNegate s2)--mathMult :: ScalarData -> ScalarData -> ScalarData-mathMult (Div m1 n1) (Div m2 n2) = mathNormalize' $ Div (mathMultPoly m1 m2) (mathMultPoly n1 n2)--mathMultPoly :: PolyExpr -> PolyExpr -> PolyExpr-mathMultPoly (Plus []) (Plus _)    = Plus []-mathMultPoly (Plus _) (Plus [])    = Plus []-mathMultPoly (Plus ts1) (Plus ts2) = foldl mathPlusPoly (Plus []) (map (\(Term a xs) -> Plus (map (\(Term b ys) -> Term (a * b) (xs ++ ys)) ts2)) ts1)--mathDiv :: ScalarData -> ScalarData -> ScalarData-mathDiv s (Div p1 p2) = mathMult s (Div p2 p1)--mathPower :: ScalarData -> Integer -> ScalarData-mathPower _ 0          = SingleTerm 1 []-mathPower s 1          = s-mathPower s n | n >= 2 = mathMult s (mathPower s (n - 1))--mathNumerator :: ScalarData -> ScalarData-mathNumerator (Div m _) = Div m (Plus [Term 1 []])--mathDenominator :: ScalarData -> ScalarData-mathDenominator (Div _ n) = Div n (Plus [Term 1 []])
+ hs-src/Language/Egison/Math/CAS.hs view
@@ -0,0 +1,1477 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternSynonyms       #-}+{-# LANGUAGE QuasiQuotes           #-}++{- |+Module      : Language.Egison.Math.CAS+Licence     : MIT++This module defines the new CASValue data type for the computer algebra system.+The type structure directly determines the runtime internal representation.++Key design principles:+- Type composition determines normal form (e.g., Poly (Frac Integer) vs Frac (Poly Integer))+- Supports Laurent polynomials (negative exponents allowed in monomials)+- Constructive: coefficients can be recursively nested CASValues+-}++module Language.Egison.Math.CAS+    ( -- * Core data types+      CASValue (..)+    , CASTerm (..)+    , Monomial+    , SymbolExpr (..)+    , Id+    , makeApplyExpr+    -- * Smart constructors+    , casInteger+    , casFactor+    , casPoly+    , casFrac+    , casTerm+    -- * Arithmetic operations+    , casPlus+    , casMult+    , casNegate+    , casMinus+    , casDivide+    , casPower+    , casNumerator+    , casDenominator+    -- * Observed type (Phase 8)+    , prettyTypeOf+    , casAtomSet+    , casDifferentialClosed+    -- * Normalization+    , casNormalize+    , casNormalizePoly+    , casReshapeAs+    -- * Re-exports for Rewrite.hs (avoids a direct Data import cycle)+    , prettyFunctionName+    -- * Predicates+    , casIsZero+    , casIsAtom+    -- * Pretty printing+    , prettyCAS+    -- * GCD operations+    , casGcd+    , casTermsGcd+    -- * Pattern synonyms for CASValue+    , pattern CASZero+    , pattern CASSingleSymbol+    , pattern CASSingleTerm+    -- * Pattern matching (control-egison)+    , CASM (..)+    , CASTermM (..)+    , CASSymbolM (..)+    , casTerm'+    , casTerm'M+    , casTermM+    , casSymbol+    , casSymbolM+    , casFunc+    , casFuncM+    , casApply1+    , casApply1M+    , casApply2+    , casApply2M+    , casApply3+    , casApply3M+    , casApply4+    , casApply4M+    , casQuote+    , casNegQuote+    , casNegQuoteM+    , casQuoteFunction+    , casQuoteFunctionM+    , casEqualMonomial+    , casEqualMonomialM+    , casZero+    , casZeroM+    , casSingleTerm+    , casSingleTermM+    ) where++import           Data.List (sortBy, groupBy, intercalate, intersect, nub)+import           Data.Ord (comparing)+import           Data.Function (on)+import           Data.Ratio ((%), numerator, denominator)++import           Control.Egison+import           Control.Monad (MonadPlus (..))++import           Language.Egison.IExpr (Index (..))+import           Language.Egison.Type.Types (Type(..), SymbolSet(..), TypeAtom(..))+import {-# SOURCE #-} Language.Egison.Data (WHNFData, prettyFunctionName)++-- | CASValue represents mathematical values in the CAS.+-- The structure is compositional: each constructor has a well-defined semantics.+data CASValue+  = CASInteger Integer+    -- ^ Base case: an integer value+  | CASFactor SymbolExpr+    -- ^ An atomic factor (generated by quote operator ')+    -- Represents a symbol or function application that is not yet expanded+  | CASPoly [CASTerm]+    -- ^ A polynomial (sum of terms). Empty list represents zero.+    -- Supports Laurent polynomials: monomial exponents can be negative.+  | CASFrac CASValue CASValue+    -- ^ A quotient: numerator / denominator+    -- Only needed when denominator is non-monomial+  deriving (Eq, Show)++-- | CASTerm represents a single term in a polynomial: coefficient × monomial+-- The coefficient is a CASValue, enabling nested polynomial structures.+data CASTerm = CASTerm CASValue Monomial+  deriving (Eq, Show)++-- | We choose the definition 'monomials' without its coefficients.+-- ex. 2 x^2 y^3 is *not* a monomial. x^2 t^3 is a monomial.+type Monomial = [(SymbolExpr, Integer)]++-- | Identifier type for symbols+type Id = String++-- | SymbolExpr represents atomic symbolic expressions in the CAS.+-- NOTE: SymbolExpr uses CASValue for function arguments (Apply1-4, Quote, FunctionData).+data SymbolExpr+  = Symbol Id String [Index CASValue]+  | Apply1 CASValue CASValue+  | Apply2 CASValue CASValue CASValue+  | Apply3 CASValue CASValue CASValue CASValue+  | Apply4 CASValue CASValue CASValue CASValue CASValue+  | Quote CASValue                     -- For backtick quote: `expr+  | QuoteFunction WHNFData             -- For single quote on functions: 'func+  | FunctionData CASValue [CASValue]   -- fnname args++-- Manual Eq instance (QuoteFunction comparison uses function name)+instance Eq SymbolExpr where+  Symbol id1 s1 js1 == Symbol id2 s2 js2 = id1 == id2 && s1 == s2 && js1 == js2+  Apply1 f1 a1 == Apply1 f2 a2 = f1 == f2 && a1 == a2+  Apply2 f1 a1 b1 == Apply2 f2 a2 b2 = f1 == f2 && a1 == a2 && b1 == b2+  Apply3 f1 a1 b1 c1 == Apply3 f2 a2 b2 c2 = f1 == f2 && a1 == a2 && b1 == b2 && c1 == c2+  Apply4 f1 a1 b1 c1 d1 == Apply4 f2 a2 b2 c2 d2 = f1 == f2 && a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2+  Quote m1 == Quote m2 = m1 == m2+  QuoteFunction whnf1 == QuoteFunction whnf2 =+    case (prettyFunctionName whnf1, prettyFunctionName whnf2) of+      (Just n1, Just n2) -> n1 == n2+      _ -> False  -- Anonymous functions are never equal+  FunctionData n1 k1 == FunctionData n2 k2 = n1 == n2 && k1 == k2+  _ == _ = False++instance Show SymbolExpr where+  show = prettySymbolExpr++-- | Pretty print a SymbolExpr+prettySymbolExpr :: SymbolExpr -> String+prettySymbolExpr (Symbol _ (':':':':':':_) []) = "#"+prettySymbolExpr (Symbol _ s [])               = s+prettySymbolExpr (Symbol _ s js)               = s ++ concatMap showIndex js+  where+    showIndex (Sup i)    = "~" ++ prettyCAS' i+    showIndex (Sub i)    = "_" ++ prettyCAS' i+    showIndex (SupSub i) = "~_" ++ prettyCAS' i+    showIndex (DF _ _)   = ""+    showIndex (User i)   = "|" ++ prettyCAS' i+prettySymbolExpr (Apply1 fn a1)                = unwords [prettyApplyFn fn, prettyApplyArg a1]+prettySymbolExpr (Apply2 fn a1 a2)             = unwords [prettyApplyFn fn, prettyApplyArg a1, prettyApplyArg a2]+prettySymbolExpr (Apply3 fn a1 a2 a3)          = unwords [prettyApplyFn fn, prettyApplyArg a1, prettyApplyArg a2, prettyApplyArg a3]+prettySymbolExpr (Apply4 fn a1 a2 a3 a4)       = unwords [prettyApplyFn fn, prettyApplyArg a1, prettyApplyArg a2, prettyApplyArg a3, prettyApplyArg a4]+prettySymbolExpr (Quote mExprs)                = "`" ++ prettyCAS' mExprs+prettySymbolExpr (QuoteFunction whnf)          = "'" ++ maybe "<function>" id (prettyFunctionName whnf)+prettySymbolExpr (FunctionData name args)      = unwords (prettyApplyFn name : map prettyApplyArg args)++-- | Pretty print the function slot of an Apply1-4 / FunctionData. The function+-- reference is often stored as a CASPoly wrapper around a single CASFactor+-- (e.g. `'cos` is `CASPoly [CASTerm 1 [(QuoteFunction cos, 1)]]`); print such+-- wrappers transparently as the underlying SymbolExpr to avoid a spurious+-- `('cos) (θ)` rendering.+prettyApplyFn :: CASValue -> String+prettyApplyFn (CASFactor sym) = prettySymbolExpr sym+prettyApplyFn (CASPoly [CASTerm (CASInteger 1) [(sym, 1)]]) = prettySymbolExpr sym+prettyApplyFn v = prettyCAS' v++-- | Pretty print an argument to an Apply or FunctionData. Single-symbol+-- arguments (lifted to CASPoly with coefficient 1) print without parens+-- (e.g. `'cos θ` instead of `'cos (θ)`); composite arguments fall back to+-- the parenthesizing `prettyCAS'`.+prettyApplyArg :: CASValue -> String+prettyApplyArg (CASFactor sym) = prettySymbolExpr sym+prettyApplyArg (CASPoly [CASTerm (CASInteger 1) [(sym, 1)]]) = prettySymbolExpr sym+prettyApplyArg v = prettyCAS' v++-- | Pretty print a CASValue (basic version for SymbolExpr Show instance)+prettyCAS :: CASValue -> String+prettyCAS (CASInteger n) = show n+prettyCAS (CASFactor sym) = prettySymbolExpr sym+prettyCAS (CASPoly []) = "0"+prettyCAS (CASPoly terms) = prettyTerms terms+  where+    prettyTerms [] = "0"+    prettyTerms (t:ts) = prettyTerm t ++ concatMap withSign ts+    withSign term@(CASTerm coeff _)+      | isNegative coeff = " - " ++ prettyTerm (negateCAST term)+      | otherwise = " + " ++ prettyTerm term+    prettyTerm (CASTerm coeff []) = prettyCAS coeff+    prettyTerm (CASTerm (CASInteger 1) mono) = prettyMono mono+    prettyTerm (CASTerm (CASInteger (-1)) mono) = "- " ++ prettyMono mono+    -- Use the parenthesizing `prettyCAS'` for the coefficient so that nested+    -- polynomial coefficients (e.g. `(2*z + 3) * x`) don't visually flatten+    -- into the surrounding sum. Integer/Factor coeffs pass through unwrapped.+    prettyTerm (CASTerm coeff mono) = prettyCAS' coeff ++ " * " ++ prettyMono mono+    -- For multi-factor monomials, use ` * ` between factors when any is a+    -- function-application form (Apply1-4 / FunctionData), since juxtaposition+    -- would be ambiguous with the function-call syntax `f x`. Otherwise keep+    -- the conventional `x y` juxtaposition for plain symbols. A single factor+    -- needs no separator nor wrapping.+    prettyMono [single] = prettyPow single+    prettyMono mono+      | any (isApplyFactor . fst) mono = intercalate " * " (map prettyPow' mono)+      | otherwise                      = unwords (map prettyPow mono)+    -- prettyPow' wraps Apply factors in parens for clarity in the explicit-`*`+    -- form (e.g. `('cos θ) * r`).+    prettyPow' (sym, 1) | isApplyFactor sym = "(" ++ prettySymbolExpr sym ++ ")"+    prettyPow' p = prettyPow p+    prettyPow (sym, 1) = prettySymbolExpr sym+    -- An application form under an exponent needs parens: `g x y^2` would+    -- read as g applied to x and y^2, not as (g x y) squared.+    prettyPow (sym, n)+      | isApplyFactor sym = "(" ++ prettySymbolExpr sym ++ ")^" ++ show n+      | otherwise         = prettyCAS' (CASFactor sym) ++ "^" ++ show n+    isApplyFactor :: SymbolExpr -> Bool+    isApplyFactor (Apply1 {})       = True+    isApplyFactor (Apply2 {})       = True+    isApplyFactor (Apply3 {})       = True+    isApplyFactor (Apply4 {})       = True+    isApplyFactor (FunctionData {}) = True+    isApplyFactor _                 = False+    isNegative (CASInteger n) = n < 0+    isNegative _ = False+    negateCAST (CASTerm (CASInteger n) m) = CASTerm (CASInteger (-n)) m+    negateCAST t = t+prettyCAS (CASFrac num denom) = prettyCAS' num ++ " / " ++ prettyCAS' denom++prettyCAS' :: CASValue -> String+prettyCAS' v@(CASInteger _) = prettyCAS v+prettyCAS' v@(CASFactor _) = prettyCAS v+prettyCAS' v = "(" ++ prettyCAS v ++ ")"++-- | Compute the observed type of a CASValue and pretty print it.+-- The observed type is the most specific static type that the value+-- inhabits, computed bottom-up from the runtime structure.+--   CASInteger _      → "Integer"+--   CASFactor (Symbol ...)+--                     → "Symbol"+--   CASFactor _       → "Factor"+--   CASPoly []        → "Integer"   (canonical zero)+--   CASPoly terms     → "Poly C [atoms]" where C is the join of term coefficient+--                       types and atoms is the sorted list of distinct flat atoms+--   CASFrac n d       → "Frac (typeOf n)" if d is integer/poly with single term,+--                       otherwise "Frac (Poly typeOf-num [..])"+prettyTypeOf :: CASValue -> String+prettyTypeOf (CASInteger _) = "Integer"+prettyTypeOf (CASFactor (Symbol _ _ _)) = "Symbol"+prettyTypeOf (CASFactor _) = "Factor"+prettyTypeOf (CASPoly []) = "Integer"+prettyTypeOf (CASPoly terms) =+  let coeffTypes = map (\(CASTerm c _) -> prettyTypeOf c) terms+      coeffType  = joinObservedTypes coeffTypes+      atoms      = collectAtoms terms+      atomStr    = if null atoms+                     then "[]"+                     else "[" ++ commaSep atoms ++ "]"+   in "Poly " ++ parenIfApp coeffType ++ " " ++ atomStr+prettyTypeOf (CASFrac n d) =+  "Frac " ++ parenIfApp inner+  where+    nT = prettyTypeOf n+    dT = prettyTypeOf d+    -- If numerator and denominator share the observed type, that's the inner;+    -- otherwise widen to MathValue.+    inner = if nT == dT then nT else "MathValue"++-- | Pretty join of observed types of multiple coefficients.+--+-- "Integer" is the bottom of the CAS observed-type lattice (it embeds into+-- every other CAS type), so when one term is observed as "Integer" and the+-- rest as some richer type T, we report T (not the over-broad "MathValue").+-- If two truly distinct non-Integer types appear, we still widen to+-- "MathValue" — a full subtype-aware join over the observed-type strings is+-- left as future work.+joinObservedTypes :: [String] -> String+joinObservedTypes []  = "Integer"+joinObservedTypes ts  = case filter (/= "Integer") ts of+  []                        -> "Integer"+  t : ts' | all (== t) ts'  -> t+          | otherwise       -> "MathValue"++-- | Phase 8 differential closure: collect the set of atoms (as their canonical+-- pretty form) appearing in a CASValue's monomials, recursing into nested+-- coefficients. The result is a sorted list of unique atom names.+casAtomSet :: CASValue -> [String]+casAtomSet (CASInteger _) = []+casAtomSet (CASFactor sym) = [prettySymbolExpr sym]+casAtomSet (CASPoly terms) =+  let atomNames = concat+        [ map (prettySymbolExpr . fst) mono ++ casAtomSet coeff+        | CASTerm coeff mono <- terms+        ]+  in unique (sortBy compare atomNames)+  where+    unique [] = []+    unique (x:xs) = x : unique (dropWhile (== x) xs)+casAtomSet (CASFrac n d) =+  let combined = casAtomSet n ++ casAtomSet d+  in unique (sortBy compare combined)+  where+    unique [] = []+    unique (x:xs) = x : unique (dropWhile (== x) xs)++-- | Check whether differentiation preserved the atom set of the input.+-- Used by the `differentialClosed` primitive: the result is true iff the+-- atom set of `output` is a subset of that of `input` (no new atoms+-- introduced by `∂/∂`).+casDifferentialClosed :: CASValue -> CASValue -> Bool+casDifferentialClosed input output =+  let inA  = casAtomSet input+      outA = casAtomSet output+  in all (`elem` inA) outA++-- | Collect distinct atom names from a list of CASTerm monomials.+-- Returns a sorted list of pretty atom forms (`x`, `sin x`, etc.).+collectAtoms :: [CASTerm] -> [String]+collectAtoms terms =+  let atomNames = [prettySymbolExpr s | CASTerm _ mono <- terms, (s, _) <- mono]+  in unique (sortBy compare atomNames)+  where+    unique [] = []+    unique (x:xs) = x : unique (dropWhile (== x) xs)++-- | Comma-separate strings.+commaSep :: [String] -> String+commaSep []     = ""+commaSep [x]    = x+commaSep (x:xs) = x ++ ", " ++ commaSep xs++-- | Wrap in parens if the type printed contains a space (i.e. an application).+parenIfApp :: String -> String+parenIfApp s+  | ' ' `elem` s = "(" ++ s ++ ")"+  | otherwise    = s++-- | Helper function to create Apply constructors based on argument count+makeApplyExpr :: CASValue -> [CASValue] -> SymbolExpr+makeApplyExpr fn [a1] = Apply1 fn a1+makeApplyExpr fn [a1, a2] = Apply2 fn a1 a2+makeApplyExpr fn [a1, a2, a3] = Apply3 fn a1 a2 a3+makeApplyExpr fn [a1, a2, a3, a4] = Apply4 fn a1 a2 a3 a4+makeApplyExpr _ _ = error "makeApplyExpr: unsupported number of arguments (must be 1-4)"++--------------------------------------------------------------------------------+-- Smart Constructors+--------------------------------------------------------------------------------++-- | Create an integer CASValue+casInteger :: Integer -> CASValue+casInteger = CASInteger++-- | Create a factor CASValue from a SymbolExpr+casFactor :: SymbolExpr -> CASValue+casFactor = CASFactor++-- | Create a polynomial CASValue, normalizing the terms+casPoly :: [CASTerm] -> CASValue+casPoly terms = casNormalize (CASPoly terms)++-- | Create a division CASValue, simplifying if possible+casFrac :: CASValue -> CASValue -> CASValue+casFrac num denom = casNormalize (CASFrac num denom)++-- | Create a term+casTerm :: CASValue -> Monomial -> CASTerm+casTerm = CASTerm++--------------------------------------------------------------------------------+-- Predicates+--------------------------------------------------------------------------------++-- | Check if a CASValue is zero+casIsZero :: CASValue -> Bool+casIsZero (CASInteger 0) = True+casIsZero (CASPoly [])   = True+casIsZero (CASFrac n _)   = casIsZero n+casIsZero _              = False++-- | Check if a CASValue is one+casIsOne :: CASValue -> Bool+casIsOne (CASInteger 1)                     = True+casIsOne (CASPoly [CASTerm (CASInteger 1) []]) = True+casIsOne _                                  = False++-- | Check if a CASValue is atomic (no parentheses needed for display)+-- Returns True for atomic values that don't need parentheses for display+casIsAtom :: CASValue -> Bool+casIsAtom (CASInteger _) = True+casIsAtom (CASFactor _)  = True+casIsAtom (CASPoly [])   = True   -- Zero+casIsAtom (CASPoly [CASTerm _ []])  = True   -- Integer only+casIsAtom (CASPoly [CASTerm (CASInteger 1) [_]]) = True  -- Single symbol with coeff 1+casIsAtom (CASFrac num (CASPoly [CASTerm (CASInteger 1) []])) = casIsAtom num  -- n/1 = n+casIsAtom _ = False++--------------------------------------------------------------------------------+-- Arithmetic Operations+--------------------------------------------------------------------------------++-- | Add two CASValues+casPlus :: CASValue -> CASValue -> CASValue+casPlus a b = casNormalize (casPlus' a b)++casPlus' :: CASValue -> CASValue -> CASValue+-- Integer + Integer+casPlus' (CASInteger a) (CASInteger b) = CASInteger (a + b)++-- Poly + Poly+casPlus' (CASPoly ts1) (CASPoly ts2) = CASPoly (ts1 ++ ts2)++-- Integer + Poly: embed integer as polynomial term+casPlus' (CASInteger n) (CASPoly ts) = CASPoly (CASTerm (CASInteger n) [] : ts)+casPlus' (CASPoly ts) (CASInteger n) = CASPoly (CASTerm (CASInteger n) [] : ts)++-- Frac + Frac: cross-multiply and add numerators+casPlus' (CASFrac n1 d1) (CASFrac n2 d2) =+  CASFrac (casPlus' (casMult' n1 d2) (casMult' n2 d1)) (casMult' d1 d2)++-- Frac + other: embed other as Frac+casPlus' (CASFrac n d) other = CASFrac (casPlus' n (casMult' other d)) d+casPlus' other (CASFrac n d) = CASFrac (casPlus' (casMult' other d) n) d++-- Factor handling: lift to polynomial before operation+casPlus' (CASFactor sym) other = casPlus' (liftFactorToPoly sym) other+casPlus' other (CASFactor sym) = casPlus' other (liftFactorToPoly sym)++-- | Negate a CASValue+casNegate :: CASValue -> CASValue+casNegate (CASInteger n)  = CASInteger (-n)+casNegate (CASPoly terms) = CASPoly (map negateTerm terms)+  where+    negateTerm (CASTerm coeff mono) = CASTerm (casNegate coeff) mono+casNegate (CASFrac n d)    = CASFrac (casNegate n) d+casNegate (CASFactor sym) = CASPoly [CASTerm (CASInteger (-1)) [(sym, 1)]]++-- | Subtract two CASValues+casMinus :: CASValue -> CASValue -> CASValue+casMinus a b = casPlus a (casNegate b)++-- | Multiply two CASValues+casMult :: CASValue -> CASValue -> CASValue+casMult a b = casNormalize (casMult' a b)++casMult' :: CASValue -> CASValue -> CASValue+-- Integer * Integer+casMult' (CASInteger a) (CASInteger b) = CASInteger (a * b)++-- Integer * Poly: scale all coefficients+casMult' (CASInteger n) (CASPoly ts) = CASPoly (map (scaleTerm n) ts)+  where+    scaleTerm k (CASTerm coeff mono) = CASTerm (casMult' (CASInteger k) coeff) mono+casMult' (CASPoly ts) (CASInteger n) = casMult' (CASInteger n) (CASPoly ts)++-- Poly * Poly: distribute+casMult' (CASPoly []) _ = CASPoly []+casMult' _ (CASPoly []) = CASPoly []+casMult' (CASPoly ts1) (CASPoly ts2) =+  CASPoly [multTerms t1 t2 | t1 <- ts1, t2 <- ts2]+  where+    multTerms (CASTerm c1 m1) (CASTerm c2 m2) =+      CASTerm (casMult' c1 c2) (combineMonomials m1 m2)++-- Frac * Frac: multiply numerators and denominators+casMult' (CASFrac n1 d1) (CASFrac n2 d2) =+  CASFrac (casMult' n1 n2) (casMult' d1 d2)++-- Frac * other: multiply into numerator+casMult' (CASFrac n d) other = CASFrac (casMult' n other) d+casMult' other (CASFrac n d) = CASFrac (casMult' other n) d++-- Factor handling: lift to polynomial before operation+casMult' (CASFactor sym) other = casMult' (liftFactorToPoly sym) other+casMult' other (CASFactor sym) = casMult' other (liftFactorToPoly sym)++-- | Lift a Factor to a polynomial: sym → 1 * sym^1+liftFactorToPoly :: SymbolExpr -> CASValue+liftFactorToPoly sym = CASPoly [CASTerm (CASInteger 1) [(sym, 1)]]++-- | Combine two monomials by adding exponents of matching symbols+combineMonomials :: Monomial -> Monomial -> Monomial+combineMonomials m1 m2 = foldr insertSymbol m2 m1+  where+    insertSymbol (sym, expo) mono =+      case lookup sym mono of+        Just _  -> map (\(s, e) -> if s == sym then (s, e + expo) else (s, e)) mono+        Nothing -> (sym, expo) : mono++-- | Divide two CASValues: a / b+casDivide :: CASValue -> CASValue -> CASValue+casDivide a b = casNormalize (CASFrac a b)++-- | Raise a CASValue to an integer power+casPower :: CASValue -> Integer -> CASValue+casPower _ 0 = CASInteger 1+casPower x 1 = x+casPower x n+  | n > 0     = casMult x (casPower x (n - 1))+  | otherwise = casDivide (CASInteger 1) (casPower x (-n))  -- Negative power++-- | Get the numerator of a CASValue.+-- For tower-fixed level 4 polynomials (Poly with Frac coefficients),+-- compute the LCM of coefficient denominators and return the value+-- multiplied by it (clearing the Fracs from coefficients).+casNumerator :: CASValue -> CASValue+casNumerator (CASFrac num _) = num+casNumerator x@(CASPoly ts) =+  let lcmD = polyDenominatorLCM ts+  in if lcmD == 1+     then x+     else casMult x (CASInteger lcmD)+casNumerator x              = x++-- | Get the denominator of a CASValue.+-- For tower-fixed level 4 polynomials, the denominator is the LCM of+-- the Frac coefficients' denominators.+casDenominator :: CASValue -> CASValue+casDenominator (CASFrac _ denom) = denom+casDenominator (CASPoly ts) = CASInteger (polyDenominatorLCM ts)+casDenominator _                = CASInteger 1++-- | Compute the LCM of all denominators in a polynomial's Frac coefficients.+-- Returns 1 if there are no Frac coefficients.+polyDenominatorLCM :: [CASTerm] -> Integer+polyDenominatorLCM ts =+  let denoms = concatMap termDenoms ts+  in if null denoms then 1 else foldl1 lcm denoms+  where+    termDenoms (CASTerm (CASFrac _ (CASInteger d)) _) = [abs d]+    termDenoms _                                       = []++--------------------------------------------------------------------------------+-- Normalization+--------------------------------------------------------------------------------++-- | Normalize a CASValue+casNormalize :: CASValue -> CASValue+casNormalize (CASInteger n) = CASInteger n+casNormalize (CASFactor sym) = CASFactor sym+casNormalize (CASPoly terms) = casNormalizePoly terms+casNormalize (CASFrac num denom) = casNormalizeFrac num denom++-- | Normalize a polynomial+-- Steps:+-- 1. Fold symbols within each term (x * x^2 → x^3)+-- 2. Remove zero-exponent symbols+-- 3. Fold terms with equal monomials+-- 4. Remove zero-coefficient terms+-- 5. Sort terms in descending order+casNormalizePoly :: [CASTerm] -> CASValue+casNormalizePoly = casNormalizePolyWith FlattenNested++-- | How polynomial normalization treats nested coefficients (Phase+-- gamma-prime of the extensible-tower plan,+-- design/type-cas-tower-implementation.md section 4).+data NestedCoeffPolicy+  = FlattenNested+    -- ^ The default on every arithmetic path: a coefficient that is itself+    -- a CASPoly (the nested canonical form produced by reshape) is+    -- distributed out into the outer monomial, so operations always exit+    -- in the default flat canonical form and terms coming from nested and+    -- flat representations merge (i + (-i) = 0 across representations).+  | KeepNested+    -- ^ Reshape's final grouping: keep the nested form just constructed.+    -- Coefficients are neither re-normalized (they were just produced by a+    -- recursive reshape; re-normalizing would flatten deeper nesting) nor+    -- distributed. Invariant: nested forms exist only as the direct output+    -- of reshape (annotation sites).+  deriving (Eq)++-- | Core polynomial normalization, parametrized by the nested-coefficient+-- policy (see 'NestedCoeffPolicy').+casNormalizePolyWith :: NestedCoeffPolicy -> [CASTerm] -> CASValue+casNormalizePolyWith policy terms =+  let flatten = policy == FlattenNested+      -- Normalize each term's monomial+      terms1 = map (normalizeTermMonomialWith flatten) terms+      -- Distribute nested coefficients (flat exit form) when asked to+      terms1' = if flatten && any hasNestedCoeff terms1+                  then map (normalizeTermMonomialWith flatten)+                           (concatMap flattenNestedTerm terms1)+                  else terms1+      -- Fold terms with equal monomials+      terms2 = foldTermsWith policy terms1'+      -- Remove zero-coefficient terms+      terms3 = filter (not . isZeroTerm) terms2+      -- Sort in descending order (standard polynomial order)+      terms4 = sortTermsDescending terms3+  in case terms4 of+       []  -> CASInteger 0  -- Empty polynomial is zero+       [CASTerm coeff []] | isIntegerCoeff coeff -> extractInteger coeff+       ts  -> CASPoly ts+  where+    isZeroTerm (CASTerm coeff _) = casIsZero coeff+    isIntegerCoeff (CASInteger _) = True+    isIntegerCoeff _ = False+    extractInteger (CASInteger n) = CASInteger n+    extractInteger _ = error "extractInteger: not an integer"++-- | Does the term carry a nested coefficient that 'flattenNestedTerm'+-- would distribute? Cheap constructor check used as a fast-path guard.+hasNestedCoeff :: CASTerm -> Bool+hasNestedCoeff (CASTerm (CASPoly _) _) = True+hasNestedCoeff (CASTerm (CASFrac (CASPoly _) (CASInteger _)) _) = True+hasNestedCoeff _ = False++-- | Distribute a nested coefficient into flat terms: a CASPoly coefficient+-- is multiplied out into the outer monomial; a CASFrac coefficient with a+-- CASPoly numerator over an integer denominator is distributed likewise.+-- Recursion terminates because each step strictly reduces nesting.+flattenNestedTerm :: CASTerm -> [CASTerm]+flattenNestedTerm t@(CASTerm c mono) = case c of+  CASPoly inner ->+    concatMap (\(CASTerm ic im) -> flattenNestedTerm (CASTerm ic (im ++ mono))) inner+  CASFrac (CASPoly inner) d@(CASInteger _) ->+    concatMap (\(CASTerm ic im) ->+                 flattenNestedTerm (CASTerm (casNormalizeFrac ic d) (im ++ mono))) inner+  _ -> [t]++-- | Normalize a term's monomial: combine duplicate symbols, remove zero+-- exponents. When `renormCoeff` is False the coefficient is left untouched+-- (reshape's keep-nested mode — the coefficient was just built by a+-- recursive reshape and re-normalizing it would flatten deeper nesting).+normalizeTermMonomialWith :: Bool -> CASTerm -> CASTerm+normalizeTermMonomialWith renormCoeff (CASTerm coeff mono) =+  let -- Fold duplicate symbols+      mono1 = foldMonomialSymbols mono+      -- Remove zero-exponent symbols+      mono2 = filter (\(_, exp) -> exp /= 0) mono1+      -- Normalize the coefficient recursively (flatten mode only)+      coeff' = if renormCoeff then casNormalize coeff else coeff+  in CASTerm coeff' mono2++-- | Fold duplicate symbols in a monomial by adding their exponents+foldMonomialSymbols :: Monomial -> Monomial+foldMonomialSymbols mono =+  let grouped = groupBy ((==) `on` fst) (sortBy (comparing (show . fst)) mono)+  in concatMap combineGroup grouped+  where+    combineGroup :: [(SymbolExpr, Integer)] -> [(SymbolExpr, Integer)]+    combineGroup [] = []+    combineGroup grp@((sym, _):_) = [(sym, sum (map snd grp))]++-- | Fold terms with equal monomials by adding their coefficients; under+-- 'KeepNested' merged coefficients are normalized without flattening, so a+-- nested coefficient produced by reshape survives the grouping.+foldTermsWith :: NestedCoeffPolicy -> [CASTerm] -> [CASTerm]+foldTermsWith policy terms =+  let grouped = groupBy equalMonos (sortBy (comparing termMonoKey) terms)+  in concatMap combineTerms grouped+  where+    -- Use show-based key for consistent ordering+    termMonoKey (CASTerm _ m) = map (\(s, e) -> (show s, e)) (sortBy (comparing (show . fst)) m)+    termMono (CASTerm _ m) = sortBy (comparing (show . fst)) m+    equalMonos t1 t2 = termMono t1 == termMono t2+    normalizeCoeff c = case (policy, c) of+      (KeepNested, CASPoly ts) -> casNormalizePolyWith KeepNested ts+      _                        -> casNormalize c+    combineTerms [] = []+    combineTerms grp@((CASTerm _ m):_) =+      let mono = sortBy (comparing (show . fst)) m+          coeffSum = foldr casPlus' (CASInteger 0) [c | CASTerm c _ <- grp]+      in [CASTerm (normalizeCoeff coeffSum) mono]++-- | Sort terms in descending order (highest degree first)+-- Order: by total degree, then lexicographically by symbols+sortTermsDescending :: [CASTerm] -> [CASTerm]+sortTermsDescending = sortBy (flip (comparing termDegree) <> flip (comparing termSymbolsKey))+  where+    termDegree (CASTerm _ mono) = sum (map snd mono)+    -- Use show-based key for consistent lexicographic ordering+    termSymbolsKey (CASTerm _ mono) = map (\(s, e) -> (show s, e)) (sortBy (comparing (show . fst)) mono)++-- | Normalize a fraction+-- Steps:+-- 1. If denominator is 1, return numerator+-- 2. If numerator is 0, return 0+-- 3. Simplify using GCD+-- 4. Ensure positive denominator+casNormalizeFrac :: CASValue -> CASValue -> CASValue+casNormalizeFrac num denom =+  let num' = casNormalize num+      denom' = casNormalize denom+  in case (num', denom') of+       -- Zero numerator+       (n, _) | casIsZero n -> CASInteger 0+       -- Denominator is 1+       (n, d) | casIsOne d -> n+       -- Denominator is -1: negate numerator+       (n, CASInteger (-1)) -> casNegate n+       -- Integer / Integer: reduce by GCD and normalize sign+       (CASInteger n, CASInteger d) ->+         let g = gcd n d+             -- Normalize sign: ensure positive denominator+             sign = if d < 0 then -1 else 1+             n' = sign * (n `div` g)+             d' = abs (d `div` g)+         in if d' == 1+            then CASInteger n'+            else CASFrac (CASInteger n') (CASInteger d')+       -- Poly / Integer (constant denominator): per the type-promotion-tower+       -- design (type-cas.md §実行時の型昇格タワー), constant denominators are+       -- absorbed into each term's coefficient as a Frac. The result is+       -- level 4 (Poly with Frac coefficients) instead of level 5 (Frac of+       -- Poly), matching the canonical form for `Poly (Frac Integer) [..]`.+       (CASPoly ts1, CASInteger d) | d /= 0 ->+         let ts1' = map (\(CASTerm c m) ->+                           CASTerm (casNormalizeFrac c (CASInteger d)) m) ts1+         in casNormalizePoly ts1'+       -- Any / single-term-Poly with monomial denominator: per the+       -- type-promotion-tower design, monomial denominators are absorbed+       -- as negative exponents (Laurent polynomial form, level 3/4) rather+       -- than left as level 5 Frac. The numerator is normalized to Poly+       -- form (CASInteger n → [CASTerm n []], CASFactor sym → [CASTerm 1+       -- [(sym, 1)]]) and each term's exponents are decremented by the+       -- denominator's monomial.+       (numV, CASPoly [CASTerm denomCoef denomMono]) | not (null denomMono) ->+         let numTerms = case numV of+               CASPoly ts          -> ts+               CASInteger n        -> [CASTerm (CASInteger n) []]+               CASFactor sym       -> [CASTerm (CASInteger 1) [(sym, 1)]]+               _                   -> [CASTerm numV []]+             ts' = map (divTermByMonomial denomCoef denomMono) numTerms+         in casNormalizePoly ts'+       -- Poly / Poly (non-monomial denominator): try to reduce by monomial GCD,+       -- then by the univariate polynomial GCD; otherwise keep as level 5 Frac.+       -- After a proper polynomial-GCD reduction we re-enter casNormalizeFrac:+       -- the reduced pair is coprime (the second pass finds a constant GCD and+       -- stops), and a denominator that collapsed to a constant or a monomial+       -- is absorbed by the earlier branches.+       (CASPoly ts1, CASPoly ts2) ->+         let (ts1', ts2') = simplifyPolyDiv ts1 ts2+         in case (ts1', ts2') of+              (ts1'', [CASTerm (CASInteger 1) []]) -> casNormalizePoly ts1''+              _ -> case univariateGcdReduce ts1' ts2' of+                     Just (ts1'', ts2'') ->+                       casNormalizeFrac (casNormalizePoly ts1'') (casNormalizePoly ts2'')+                     Nothing -> case multivariateGcdReduce ts1' ts2' of+                       Just (ts1'', ts2'') ->+                         casNormalizeFrac (casNormalizePoly ts1'') (casNormalizePoly ts2'')+                       Nothing -> CASFrac (casNormalizePoly ts1') (casNormalizePoly ts2')+       -- a / (b / c) = (a * c) / b+       (n, CASFrac b c) -> casNormalizeFrac (casMult n c) b+       -- (a / b) / c = a / (b * c)+       (CASFrac a b, c) -> casNormalizeFrac a (casMult b c)+       -- Default: no simplification+       _ -> CASFrac num' denom'++--------------------------------------------------------------------------------+-- Type-driven reshape (Phase A of the reshape primitive design)+--------------------------------------------------------------------------------++-- | Reshape a CAS value to match the structure implied by the given Type.+--+-- This is the runtime side of the `reshape` primitive: type info comes from+-- a compile-time annotation embedded in the AST, and at evaluation time we+-- structurally rewrite the CASValue to fit that shape.+--+-- The implementation follows the type promotion tower (see type-cas-tower.md):+-- normalize first (which handles tower-level reductions), then recursively+-- adjust coefficients/numerators/denominators to match nested type arguments.+-- For values that cannot be reshaped to the target structure (e.g. a value+-- with free atoms reshaped to TInt), we leave the normalized form as-is —+-- per the "trust the annotation" principle.+casReshapeAs :: Type -> CASValue -> CASValue+casReshapeAs ty v = case ty of+  TInt           -> casNormalize v+  TMathValue     -> casNormalize v+  TFactor        -> casNormalize v+  TFrac inner    -> reshapeAsFrac inner v+  TPoly inner ss -> reshapeAsPoly inner ss v+  TTerm inner ss -> reshapeAsTerm inner ss v+  _              -> v  -- non-CAS types: pass through++-- | Frac inner: keep fraction form (or collapsed Integer when denom=1) and+-- recursively reshape numerator and denominator with the inner type.+reshapeAsFrac :: Type -> CASValue -> CASValue+reshapeAsFrac innerTy v = case casNormalize v of+  CASInteger n      -> CASInteger n+  CASFrac num denom -> casNormalizeFrac (casReshapeAs innerTy num)+                                        (casReshapeAs innerTy denom)+  cv                -> cv++-- | Poly inner [..]: structural reshape with atom-set separation.+--+-- Atom routing is driven by the whole inner tower — the chain of nested+-- Poly/Term coefficient types, descending through Frac — under the+-- restriction that a nested Poly tower contains AT MOST ONE open symbol+-- set `[..]` (checked statically at annotation and declaration sites,+-- Types.hasAmbiguousOpenTower):+--+--   * The inner tower is all closed (e.g. `Poly (Poly Integer [i]) [x]`,+--     or with an open OUTER set `Poly (Poly Integer [i]) [..]`): atoms+--     listed anywhere in the inner tower go into the coefficient (the deep+--     union, so towers of depth 3+ route correctly), the rest stay at this+--     level.+--   * The inner tower contains the open slot and this level's set is+--     closed (e.g. `Poly (Poly Integer [..]) [i]`): complement split —+--     atoms of this level's closed set stay here, everything else goes+--     into the coefficient.+--   * No routing information at all (base coefficient type such as+--     Integer, or both this level and the inner tower open — excluded by+--     the static check): no separation; just recurse on coefficients+--     (basic widening).+--+-- The inside atoms are folded into the coefficient (which is then+-- recursively reshaped to the inner type, repeating the same routing one+-- level deeper), the outside atoms form the new term's monomial.+-- Note (Phase gamma-prime): the entry `casNormalize v` flattens any nested+-- shape the input may carry, so reshape is a function of the VALUE only —+-- this is what makes the absorption law `casReshapeAs C . casReshapeAs B =+-- casReshapeAs C` hold (D5 coherence). The final grouping then uses the+-- keep-nested normalizer so the structure just built is not re-flattened.+reshapeAsPoly :: Type -> SymbolSet -> CASValue -> CASValue+reshapeAsPoly innerTy outerSS v = case casNormalize v of+  CASInteger n -> CASInteger n+  CASPoly ts   ->+    case atomSplit innerTy outerSS of+      Just split -> casNormalizePolyWith KeepNested (map (separateTerm innerTy split) ts)+      Nothing    -> casNormalizePolyWith KeepNested+                      [CASTerm (casReshapeAs innerTy c) m | CASTerm c m <- ts]+  cv           -> cv++-- | Term inner [...]: like Poly but expected to be a single-term form.+-- We do not enforce the single-term invariant here; the type checker is+-- responsible for that, and at runtime we just recurse.+reshapeAsTerm :: Type -> SymbolSet -> CASValue -> CASValue+reshapeAsTerm = reshapeAsPoly++-- | Which atoms of a term go into the coefficient at this Poly level.+data AtomSplit+  = InsideAtoms [TypeAtom]   -- ^ an atom goes inside iff it is listed+  | OutsideAtoms [TypeAtom]  -- ^ an atom goes inside iff it is NOT listed+                             --   (complement split for an open inner tower)++-- | Decide the atom split at one Poly level from the inner tower and this+-- level's own symbol set. Returns Nothing when there is no routing+-- information (basic widening).+atomSplit :: Type -> SymbolSet -> Maybe AtomSplit+atomSplit innerTy outerSS =+  let (innerClosed, innerOpens) = towerInfo innerTy+  in if innerOpens >= 1+       then case outerSS of+              SymbolSetClosed outerAtoms -> Just (OutsideAtoms outerAtoms)+              _                          -> Nothing+       else if null innerClosed+              then Nothing+              else Just (InsideAtoms innerClosed)++-- | Collect, over the whole inner tower (nested Poly/Term levels, descending+-- through Frac), the union of closed atom sets and the number of open+-- symbol sets.+towerInfo :: Type -> ([TypeAtom], Int)+towerInfo (TPoly inner ss) = combineSS ss (towerInfo inner)+towerInfo (TTerm inner ss) = combineSS ss (towerInfo inner)+towerInfo (TFrac inner)    = towerInfo inner+towerInfo _                = ([], 0)++combineSS :: SymbolSet -> ([TypeAtom], Int) -> ([TypeAtom], Int)+combineSS (SymbolSetClosed atoms) (as, n) = (atoms ++ as, n)+combineSS SymbolSetOpen           (as, n) = (as, n + 1)+combineSS (SymbolSetVar _)        (as, n) = (as, n)++-- | Split a single CASTerm given the atom split: the inside atoms are+-- multiplied into the coefficient and reshaped to `innTy`; the remaining+-- atoms become the new term's monomial.+separateTerm :: Type -> AtomSplit -> CASTerm -> CASTerm+separateTerm innTy split (CASTerm c mono) =+  let (innerMono, outerMono) =+        case split of+          InsideAtoms atoms  -> splitMonomialByAtoms atoms mono+          OutsideAtoms atoms -> let (out, inn) = splitMonomialByAtoms atoms mono+                                in (inn, out)+      coeffWithInner =+        if null innerMono+          then c+          else casMult c (CASPoly [CASTerm (CASInteger 1) innerMono])+      newCoeff = casReshapeAs innTy coeffWithInner+  in CASTerm newCoeff outerMono++-- | Partition a monomial: (atoms-in-inner-set, atoms-not-in-inner-set).+splitMonomialByAtoms :: [TypeAtom] -> Monomial -> (Monomial, Monomial)+splitMonomialByAtoms inAtoms = go [] []+  where+    go inAcc outAcc []                 = (reverse inAcc, reverse outAcc)+    go inAcc outAcc (e@(sym, _) : rest)+      | symbolInAtomSet sym inAtoms = go (e : inAcc) outAcc rest+      | otherwise                   = go inAcc (e : outAcc) rest++-- | Decide whether a SymbolExpr matches any TypeAtom in the inner set.+-- Matches `Symbol _ name _` against `TANameAtom name` (e.g. atom `i`).+-- Matches `Apply1..4 fn _` against `TAApplyAtom name _` by function name+-- (e.g. atom `sin x` for any single application of `sin`). The argument+-- structure inside the TypeAtom is not currently checked.+symbolInAtomSet :: SymbolExpr -> [TypeAtom] -> Bool+symbolInAtomSet sym = any (matches sym)+  where+    matches (Symbol _ name _) (TANameAtom n)    = name == n+    matches (Apply1 fn _)         (TAApplyAtom n _) = applyFnName fn == Just n+    matches (Apply2 fn _ _)       (TAApplyAtom n _) = applyFnName fn == Just n+    matches (Apply3 fn _ _ _)     (TAApplyAtom n _) = applyFnName fn == Just n+    matches (Apply4 fn _ _ _ _)   (TAApplyAtom n _) = applyFnName fn == Just n+    matches (FunctionData fn _)   (TAApplyAtom n _) = applyFnName fn == Just n+    matches _                 _                      = False++    applyFnName (CASFactor (Symbol _ n _))                                 = Just n+    applyFnName (CASPoly [CASTerm (CASInteger 1) [(Symbol _ n _, 1)]])     = Just n+    applyFnName _                                                          = Nothing++-- | Simplify polynomial division by extracting common monomial GCD+simplifyPolyDiv :: [CASTerm] -> [CASTerm] -> ([CASTerm], [CASTerm])+simplifyPolyDiv [] ts2 = ([], ts2)+simplifyPolyDiv ts1 [] = (ts1, [])+simplifyPolyDiv ts1 ts2 =+  let gcdTerm = casTermsGcd (ts1 ++ ts2)+  in (map (`divideTermBy` gcdTerm) ts1, map (`divideTermBy` gcdTerm) ts2)++-- | Divide a Term by a monomial denominator (single-term Poly's coef and mono).+-- Used for Laurent absorption: `(coef * mono) / (denomCoef * denomMono)` becomes+-- `(coef / denomCoef) * (mono - denomMono)` where exponents are subtracted.+divTermByMonomial :: CASValue -> Monomial -> CASTerm -> CASTerm+divTermByMonomial denomCoef denomMono (CASTerm c m) =+  CASTerm (casNormalizeFrac c denomCoef) (subtractMonomial m denomMono)++-- | Subtract one monomial from another (decrement exponents of shared symbols,+-- introduce negative exponents for symbols only in the divisor). Zero-exponent+-- entries are left in; `normalizeTermMonomial` will filter them out later.+subtractMonomial :: Monomial -> Monomial -> Monomial+subtractMonomial nums denoms = foldr subOne nums denoms+  where+    subOne (sym, denomExp) acc =+      case lookup sym acc of+        Just numExp ->+          (sym, numExp - denomExp) : filter ((/= sym) . fst) acc+        Nothing -> (sym, -denomExp) : acc++--------------------------------------------------------------------------------+-- GCD Operations+--------------------------------------------------------------------------------++-- | GCD of two CASValues (for coefficient reduction)+-- Initial implementation: only handles CASInteger, others return 1+casGcd :: CASValue -> CASValue -> CASValue+casGcd (CASInteger a) (CASInteger b) = CASInteger (gcd a b)+casGcd _ _ = CASInteger 1  -- Fallback: GCD = 1 for other coefficient types++-- | Compute the GCD of a list of terms (coefficient GCD + monomial GCD)+casTermsGcd :: [CASTerm] -> CASTerm+casTermsGcd [] = CASTerm (CASInteger 1) []+casTermsGcd [t] = t+casTermsGcd terms = foldl1 termGcd terms+  where+    termGcd (CASTerm c1 m1) (CASTerm c2 m2) =+      CASTerm (casGcd c1 c2) (monoGcd m1 m2)++-- | Reduce a Poly/Poly fraction by the univariate polynomial GCD over Q,+-- e.g. (x^2 - 1)/(x - 1) -> (x + 1)/1.+--+-- Stage-1 scope (design/type-cas-tower-implementation.md section 7): both+-- term lists must be univariate in the SAME single symbol with positive+-- exponents (the common monomial content has already been divided out by+-- 'simplifyPolyDiv', but per-side Laurent exponents may remain — those+-- bail out) and integer or integer-fraction coefficients. Anything else+-- returns Nothing and the fraction is left untouched.+--+-- The reduced pair is rescaled by a COMMON factor — integer coefficients,+-- joint content 1, positive leading denominator coefficient — so the+-- fraction's value is preserved exactly.+univariateGcdReduce :: [CASTerm] -> [CASTerm] -> Maybe ([CASTerm], [CASTerm])+univariateGcdReduce ts1 ts2+  | null ts1 || null ts2 = Nothing+  | otherwise = do+      sym <- singleCommonSymbol+      p1 <- toDense sym ts1+      p2 <- toDense sym ts2+      let g = polyGcdQ p1 p2+      if length g < 2  -- constant gcd: nothing to reduce+        then Nothing+        else do+          q1 <- exactDivQ p1 g+          q2 <- exactDivQ p2 g+          let l  = foldr (lcm . denominator) 1 (q1 ++ q2)+              i1 = map (numerator . (* (l % 1))) q1+              i2 = map (numerator . (* (l % 1))) q2+              c0 = foldr gcd 0 (i1 ++ i2)+              c  = if c0 == 0 then 1 else c0+              d  = case i2 of (x : _) | x < 0 -> negate c+                              _               -> c+          return (fromDense sym (map (`div` d) i1), fromDense sym (map (`div` d) i2))+  where+    -- Euclid on dense rationals is cheap for the degrees CAS code meets;+    -- the cutoff only guards against pathological inputs.+    maxGcdDegree :: Integer+    maxGcdDegree = 200++    singleCommonSymbol =+      case nub [ s | CASTerm _ m <- ts1 ++ ts2, (s, _) <- m ] of+        [s] -> Just s+        _   -> Nothing++    coefToRational (CASInteger n) = Just (n % 1)+    coefToRational (CASFrac (CASInteger a) (CASInteger b))+      | b /= 0 = Just (a % b)+    coefToRational _ = Nothing++    termExponent sym (CASTerm _ m) = case m of+      []                           -> Just 0+      [(s, e)] | s == sym && e > 0 -> Just e+      _                            -> Nothing++    -- Dense, highest-degree-first coefficient list over Q.+    toDense sym ts = do+      pairs <- mapM (\t@(CASTerm c _) ->+                       (,) <$> termExponent sym t <*> coefToRational c) ts+      let deg = maximum (map fst pairs)+      if deg > maxGcdDegree+        then Nothing+        else Just [ sum [ c | (e, c) <- pairs, e == d ] | d <- [deg, deg-1 .. 0] ]++    fromDense sym cs =+      [ CASTerm (CASInteger c) (if e == 0 then [] else [(sym, e)])+      | (e, c) <- zip [toInteger (length cs) - 1, toInteger (length cs) - 2 .. 0] cs+      , c /= 0 ]++    trim = dropWhile (== 0)++    -- Position-preserving long division (no mid-loop trimming: a zero that+    -- appears at the head after cancellation is a zero QUOTIENT coefficient,+    -- not a shorter polynomial). One quotient coefficient per step.+    polyDivModQ x0 y0 =+      let y = trim y0+          m = length y+          go r | length r < m = ([], r)+               | otherwise = case (r, y) of+                   (rh : rt, yh : yt) ->+                     let k  = rh / yh+                         r' = zipWith (-) rt+                                      (map (* k) (yt ++ replicate (length r - m) 0))+                         (qs, rest) = go r'+                     in (k : qs, rest)+                   _ -> error "polyDivModQ: division by the zero polynomial"+      in go (trim x0)++    polyGcdQ a b = go (trim a) (trim b)+      where+        go x [] = monic x+        go x y  = go y (trim (snd (polyDivModQ x y)))++    monic []        = []+    monic xs@(x0:_) = map (/ x0) xs++    exactDivQ x y =+      let (q, r) = polyDivModQ x y+      in if all (== 0) r then Just q else Nothing++-- | Reduce a Poly/Poly fraction by the multivariate polynomial GCD over the+-- rationals (subresultant PRS) — stage 2 of the fraction reduction+-- (design/cas-simplification.md G1).+--+-- Every atom (symbols, symbolic applications such as 'cos θ, quotes,+-- function symbols) is treated uniformly as a variable, so the reducer+-- covers Schwarzschild/T2/thurston-style cancellations with no extra+-- machinery. Fail-open: any input outside the supported shape (Laurent+-- exponents, non-rational coefficients, sizes beyond the guards) returns+-- Nothing and the fraction is left untouched.+--+-- Value preservation: both sides are scaled by ONE common denominator-+-- clearing factor, divided exactly by the same gcd, and finally rescaled by+-- a common integer content and sign, so the fraction's value never changes.+multivariateGcdReduce :: [CASTerm] -> [CASTerm] -> Maybe ([CASTerm], [CASTerm])+multivariateGcdReduce ts1 ts2+  | null ts1 || null ts2 = Nothing+  | otherwise = do+      rs1 <- mapM ratTerm ts1+      rs2 <- mapM ratTerm ts2+      -- Laurent exponents are out of scope (handled by the monomial layer).+      ensure (all (all ((> 0) . snd) . snd) (rs1 ++ rs2))+      let atoms = sortBy (comparing show)+                    (nub [ s | (_, m) <- rs1 ++ rs2, (s, _) <- m ])+      -- The univariate reducer owns the single-symbol case.+      ensure (length atoms >= 2 && length atoms <= mpMaxAtoms)+      let nAtoms = length atoms+          scale  = foldr (lcm . denominator . fst) 1 (rs1 ++ rs2)+          zeroV  = replicate nAtoms 0+          oneMP  = [(1, zeroV)]++          toVec m = [ sum [ e | (s, e) <- m', s == a ] | a <- atoms ]+            where m' = foldMonomialSymbols m+          conv rs = mpNorm [ (numerator (c * (scale % 1)), toVec m) | (c, m) <- rs ]+          p = conv rs1+          q = conv rs2++          -- Descending graded-lexicographic order over the fixed atom list;+          -- a proper monomial order, so leading-term exact division works.+          mpGrlex a b = compare (sum a, a) (sum b, b)+          mpNorm ts =+            [ (c, v)+            | grp@((_, v) : _) <- groupBy ((==) `on` snd)+                                    (sortBy (flip mpGrlex `on` snd) ts)+            , let c = sum (map fst grp)+            , c /= 0 ]++          mpNeg   = map (\(c, v) -> (negate c, v))+          mpAdd a b = mpNorm (a ++ b)+          mpSub a b = mpAdd a (mpNeg b)+          mpMul a b = mpNorm [ (c1 * c2, zipWith (+) v1 v2)+                             | (c1, v1) <- a, (c2, v2) <- b ]+          mpMulTerm (c, v) b = mpNorm [ (c * c2, zipWith (+) v v2) | (c2, v2) <- b ]+          mpPow b n = foldr mpMul oneMP (replicate (fromInteger n) b)++          mpMaxTotalDeg f = maximum (0 : [ sum v | (_, v) <- f ])+          mpDegIn i f     = maximum (0 : [ v !! i | (_, v) <- f ])+          mpPresent f     = [ i | i <- [0 .. nAtoms - 1]+                                , any (\(_, v) -> v !! i > 0) f ]+          mpIntContent f  = foldr (gcd . fst) 0 f++          -- Positive normalization: strip the integer content, make the+          -- leading (grlex) coefficient positive.+          mpPosNorm [] = []+          mpPosNorm f  =+            let c0 = mpIntContent f+                s  = case f of ((c, _) : _) | c < 0 -> -1+                               _                    -> 1+                d  = s * c0+            in map (\(c, v) -> (c `div` d, v)) f++          -- Leading-term exact division; Nothing when not exact.+          mpDivExact _ [] = Nothing+          mpDivExact a0 b@((bc, bv) : _) = go a0 []+            where+              go [] acc = Just (mpNorm acc)+              go a@((ac, av) : _) acc+                | all (>= 0) dv && ac `mod` bc == 0 =+                    let qt = (ac `div` bc, dv)+                    in go (mpSub a (mpMulTerm qt b)) (qt : acc)+                | otherwise = Nothing+                where dv = zipWith (-) av bv++          zeroAtI i v = take i v ++ [0] ++ drop (i + 1) v++          -- Univariate view in atom i: (degree, coefficient poly without i),+          -- degrees descending.+          mpUniView i f =+            [ (v0 !! i, mpNorm [ (c, zeroAtI i v) | (c, v) <- grp ])+            | grp@((_, v0) : _) <- groupBy ((==) `on` ((!! i) . snd))+                                     (sortBy (flip compare `on` ((!! i) . snd)) f) ]++          mpCoefAt i d f = mpNorm [ (c, zeroAtI i v) | (c, v) <- f, v !! i == d ]+          mpLeadCoef i f = mpCoefAt i (mpDegIn i f) f+          mpShift i k    = map (\(c, v) ->+                                  (c, take i v ++ [v !! i + k] ++ drop (i + 1) v))++          -- Content and primitive part with respect to atom i.+          mpContentI i f = goC (map snd (mpUniView i f))+            where+              goC []       = Just oneMP+              goC [c0]     = Just (mpPosNorm c0)+              goC (c0 : cs) = do+                rest <- goC cs+                if rest == oneMP then Just oneMP else mpGcdM (mpPosNorm c0) rest++          -- Knuth's division-free pseudo-remainder of f1 by f2 in atom i:+          -- the remainder of lc(f2)^(delta+1) * f1 divided by f2.+          mpPrem i f1 f2 =+            let dq  = mpDegIn i f2+                lcq = mpLeadCoef i f2+                step r k =+                  let ck = mpCoefAt i (dq + k) r+                      r' = mpSub (mpMul lcq r) (mpMul (mpShift i k ck) f2)+                  in r'+                go r k | length r > mpPrsTermCap = Nothing+                       | k < 0     = Just r+                       | otherwise = go (step r k) (k - 1)+            in go f1 (mpDegIn i f1 - dq)++          -- Subresultant PRS on primitive parts; returns the gcd of the+          -- primitive parts (a unit when they are coprime in atom i).+          mpPrsGcd i f1 f2+            | mpDegIn i f1 < mpDegIn i f2 = mpPrsGcd i f2 f1+            | mpDegIn i f2 == 0 = Just oneMP+            | otherwise = loop f1 f2 oneMP oneMP+            where+              loop a b g h = do+                ensure (length a <= mpPrsTermCap && length b <= mpPrsTermCap)+                let delta = mpDegIn i a - mpDegIn i b+                r <- mpPrem i a b+                if null r+                  then do c <- mpContentI i b+                          mpDivExact b c+                  else if mpDegIn i r == 0+                    then Just oneMP+                    else do+                      b' <- mpDivExact r (mpMul g (mpPow h delta))+                      let g' = mpLeadCoef i b+                      h' <- case delta of+                              0 -> Just h+                              1 -> Just g'+                              _ -> mpDivExact (mpPow g' delta)+                                              (mpPow h (delta - 1))+                      loop b b' g' h'++          -- Multivariate gcd, recursing on the set of present atoms.+          mpGcdM a b+            | null a = Just (mpPosNorm b)+            | null b = Just (mpPosNorm a)+            | otherwise =+                case mpPresent a ++ mpPresent b of+                  [] -> Just [(gcd (mpIntContent a) (mpIntContent b), zeroV)]+                  idxs -> do+                    let i = pickVar (nub idxs)+                    ca <- mpContentI i a+                    pa <- mpDivExact a ca+                    cb <- mpContentI i b+                    pb <- mpDivExact b cb+                    c  <- mpGcdM ca cb+                    g  <- mpPrsGcd i pa pb+                    Just (mpPosNorm (mpMul c g))+            where+              pickVar is = snd (minimum+                [ (mpDegIn i a + mpDegIn i b, i) | i <- is ])++          -- Deterministic coprimality prefilter: evaluate both sides at a+          -- fixed prime point; a common divisor g must satisfy g(pt) | gcd+          -- of the evaluations, so gcd 1 certifies that any common divisor+          -- is a unit at that point (heuristic skip; fail-open, and+          -- deterministic because the points are fixed).+          mpEval pt f = sum [ c * product (zipWith (^) pt v) | (c, v) <- f ]+          evalCoprime =+            any certify [ [3,5,7,11,13,17,19,23], [29,31,37,41,43,47,53,59] ]+            where+              certify pt =+                let a = mpEval (take nAtoms pt) p+                    b = mpEval (take nAtoms pt) q+                in a /= 0 && b /= 0 && gcd a b == 1++      ensure (length p <= mpMaxTerms && length q <= mpMaxTerms)+      ensure (mpMaxTotalDeg p <= mpMaxDegree && mpMaxTotalDeg q <= mpMaxDegree)+      ensure (not (null (mpPresent p `intersect` mpPresent q)))+      ensure (not evalCoprime)+      g <- mpGcdM p q+      ensure (mpMaxTotalDeg g > 0)+      pR <- mpDivExact p g+      qR <- mpDivExact q g+      -- Common rescale of the reduced pair (value-preserving): joint integer+      -- content out, denominator's leading coefficient positive.+      let cJ = gcd (mpIntContent pR) (mpIntContent qR)+          sg = case qR of ((c, _) : _) | c < 0 -> -1+                          _                    -> 1+          d  = sg * (if cJ == 0 then 1 else cJ)+          toTerms f = [ CASTerm (CASInteger (c `div` d))+                                [ (a, e) | (a, e) <- zip atoms v, e /= 0 ]+                      | (c, v) <- f ]+      return (toTerms pR, toTerms qR)+  where+    ensure b = if b then Just () else Nothing++    ratTerm (CASTerm c m) = (\r -> (r, m)) <$> ratCoef c+    ratCoef (CASInteger n) = Just (n % 1)+    ratCoef (CASFrac (CASInteger a) (CASInteger b)) | b /= 0 = Just (a % b)+    ratCoef _ = Nothing++    mpMaxAtoms  = 8+    mpMaxTerms  = 200+    mpMaxDegree = 60 :: Integer+    mpPrsTermCap = 2000++-- | GCD of two monomials: take minimum exponent for each shared symbol+monoGcd :: Monomial -> Monomial -> Monomial+monoGcd [] _ = []+monoGcd ((sym, expo):rest) mono =+  case lookup sym mono of+    Just exp' -> (sym, min expo exp') : monoGcd rest mono+    Nothing   -> monoGcd rest mono++-- | Divide a term by another term (for GCD reduction)+divideTermBy :: CASTerm -> CASTerm -> CASTerm+divideTermBy (CASTerm coeff1 mono1) (CASTerm coeff2 mono2) =+  CASTerm (divCoeff coeff1 coeff2) (divMono mono1 mono2)+  where+    divCoeff (CASInteger a) (CASInteger b) = CASInteger (a `div` b)+    divCoeff a _ = a  -- Fallback: no division for other types++    divMono m [] = m+    divMono m ((sym, expo):rest) =+      let m' = map (\(s, e) -> if s == sym then (s, e - expo) else (s, e)) m+      in divMono m' rest++--------------------------------------------------------------------------------+-- Pattern Synonyms for CASValue+--------------------------------------------------------------------------------++-- | Pattern for zero value+pattern CASZero :: CASValue+pattern CASZero = CASInteger 0++-- | Pattern for a single symbol: sym → 1 * sym^1+pattern CASSingleSymbol :: SymbolExpr -> CASValue+pattern CASSingleSymbol sym = CASPoly [CASTerm (CASInteger 1) [(sym, 1)]]++-- | Pattern for a single term: coeff * mono+pattern CASSingleTerm :: Integer -> Monomial -> CASValue+pattern CASSingleTerm coeff mono = CASPoly [CASTerm (CASInteger coeff) mono]++--------------------------------------------------------------------------------+-- Pattern Matching (control-egison matchers)+--------------------------------------------------------------------------------++-- | Matcher for CASValue+data CASM = CASM+instance Matcher CASM CASValue++-- | Matcher for CASTerm+data CASTermM = CASTermM+instance Matcher CASTermM CASTerm++-- | Matcher for SymbolExpr (CAS version)+data CASSymbolM = CASSymbolM+instance Matcher CASSymbolM SymbolExpr++-- | Match a term and extract its coefficient and monomial+casTerm' :: Pattern (PP CASValue, PP Monomial) CASTermM CASTerm (CASValue, Monomial)+casTerm' _ _ (CASTerm coeff mono) = pure (coeff, mono)+-- | Matcher decomposition for casTerm' pattern+casTerm'M :: CASTermM -> CASTerm -> (CASM, Multiset (CASSymbolM, Eql))+casTerm'M CASTermM _ = (CASM, Multiset (CASSymbolM, Eql))+casTermM :: CASTermM -> CASTerm -> (CASM, Multiset (CASSymbolM, Eql))+casTermM CASTermM _ = (CASM, Multiset (CASSymbolM, Eql))++-- | Match a symbol and extract its name+casSymbol :: Pattern (PP String) CASSymbolM SymbolExpr String+casSymbol _ _ (Symbol _ name []) = pure name+casSymbol _ _ _                  = mzero+casSymbolM :: CASSymbolM -> p -> Eql+casSymbolM CASSymbolM _ = Eql++-- | Match a function and extract its name and arguments+casFunc :: Pattern (PP CASValue, PP [CASValue])+                CASSymbolM SymbolExpr (CASValue, [CASValue])+casFunc _ _ (FunctionData name args) = pure (name, args)+casFunc _ _ _                        = mzero+casFuncM :: CASSymbolM -> SymbolExpr -> (CASM, List CASM)+casFuncM CASSymbolM _ = (CASM, List CASM)++-- | Match Apply1 and extract function name, WHNF, and argument+casApply1 :: Pattern (PP String, PP WHNFData, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue)+casApply1 _ _ (Apply1 (CASSingleSymbol (QuoteFunction fnWhnf)) a1) =+  case prettyFunctionName fnWhnf of+    Just fn -> pure (fn, fnWhnf, a1)+    Nothing -> mzero+casApply1 _ _ _ = mzero+casApply1M :: CASSymbolM -> p -> (Eql, Something, CASM)+casApply1M CASSymbolM _ = (Eql, Something, CASM)++-- | Match Apply2 and extract function name, WHNF, and arguments+casApply2 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue)+casApply2 _ _ (Apply2 (CASSingleSymbol (QuoteFunction fnWhnf)) a1 a2) =+  case prettyFunctionName fnWhnf of+    Just fn -> pure (fn, fnWhnf, a1, a2)+    Nothing -> mzero+casApply2 _ _ _ = mzero+casApply2M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM)+casApply2M CASSymbolM _ = (Eql, Something, CASM, CASM)++-- | Match Apply3 and extract function name, WHNF, and arguments+casApply3 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue, CASValue)+casApply3 _ _ (Apply3 (CASSingleSymbol (QuoteFunction fnWhnf)) a1 a2 a3) =+  case prettyFunctionName fnWhnf of+    Just fn -> pure (fn, fnWhnf, a1, a2, a3)+    Nothing -> mzero+casApply3 _ _ _ = mzero+casApply3M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM, CASM)+casApply3M CASSymbolM _ = (Eql, Something, CASM, CASM, CASM)++-- | Match Apply4 and extract function name, WHNF, and arguments+casApply4 :: Pattern (PP String, PP WHNFData, PP CASValue, PP CASValue, PP CASValue, PP CASValue) CASSymbolM SymbolExpr (String, WHNFData, CASValue, CASValue, CASValue, CASValue)+casApply4 _ _ (Apply4 (CASSingleSymbol (QuoteFunction fnWhnf)) a1 a2 a3 a4) =+  case prettyFunctionName fnWhnf of+    Just fn -> pure (fn, fnWhnf, a1, a2, a3, a4)+    Nothing -> mzero+casApply4 _ _ _ = mzero+casApply4M :: CASSymbolM -> p -> (Eql, Something, CASM, CASM, CASM, CASM)+casApply4M CASSymbolM _ = (Eql, Something, CASM, CASM, CASM, CASM)++-- | Match Quote and extract the inner CASValue+casQuote :: Pattern (PP CASValue) CASSymbolM SymbolExpr CASValue+casQuote _ _ (Quote m) = pure m+casQuote _ _ _         = mzero++-- | Match Quote and extract the negated inner CASValue+casNegQuote :: Pattern (PP CASValue) CASSymbolM SymbolExpr CASValue+casNegQuote _ _ (Quote m) = pure (casNegate m)+casNegQuote _ _ _         = mzero+casNegQuoteM :: CASSymbolM -> p -> CASM+casNegQuoteM CASSymbolM _ = CASM++-- | Match QuoteFunction and extract function name and WHNF+casQuoteFunction :: Pattern (PP String, PP WHNFData) CASSymbolM SymbolExpr (String, WHNFData)+casQuoteFunction _ _ (QuoteFunction whnf) = case prettyFunctionName whnf of+  Just name -> pure (name, whnf)+  Nothing   -> mzero+casQuoteFunction _ _ _ = mzero+casQuoteFunctionM :: CASSymbolM -> p -> Eql+casQuoteFunctionM CASSymbolM _ = Eql++-- | Match equal monomial (checks if two monomials are equal, handling sign)+casEqualMonomial :: Pattern (PP Integer, PP Monomial) (Multiset (CASSymbolM, Eql)) Monomial (Integer, Monomial)+casEqualMonomial (_, VP xs) _ ys = case casIsEqualMonomial xs ys of+                                  Just sgn -> pure (sgn, xs)+                                  Nothing  -> mzero+casEqualMonomial _ _ _ = mzero+casEqualMonomialM :: Multiset (CASSymbolM, Eql) -> p -> (Eql, Multiset (CASSymbolM, Eql))+casEqualMonomialM (Multiset (CASSymbolM, Eql)) _ = (Eql, Multiset (CASSymbolM, Eql))++-- | Check if two monomials are equal, returning sign if so+casIsEqualMonomial :: Monomial -> Monomial -> Maybe Integer+casIsEqualMonomial xs ys =+  match dfs (xs, ys) (Multiset (CASSymbolM, Eql), Multiset (CASSymbolM, Eql))+    [ [mc| ((casQuote $s, $n) : $xss, (casNegQuote #s, #n) : $yss) ->+             case casIsEqualMonomial xss yss of+               Nothing -> Nothing+               Just sgn -> return (if even n then sgn else - sgn) |]+    , [mc| (($x, $n) : $xss, (#x, #n) : $yss) -> casIsEqualMonomial xss yss |]+    , [mc| ([], []) -> return 1 |]+    , [mc| _ -> Nothing |]+    ]++-- | Match zero CASValue+casZero :: Pattern () CASM CASValue ()+casZero _ _ CASZero    = pure ()+casZero _ _ (CASPoly []) = pure ()  -- Empty polynomial is also zero+casZero _ _ _          = mzero+casZeroM :: CASM -> p -> ()+casZeroM CASM _ = ()++-- | Match a single term in CASValue and extract coefficient, denominator coefficient, and monomial+casSingleTerm :: Pattern (PP Integer, PP Integer, PP Monomial) CASM CASValue (Integer, Integer, Monomial)+casSingleTerm _ _ (CASFrac (CASPoly [CASTerm (CASInteger c) mono]) (CASPoly [CASTerm (CASInteger c2) []])) = pure (c, c2, mono)+casSingleTerm _ _ (CASFrac (CASSingleTerm c mono) (CASInteger c2)) = pure (c, c2, mono)+casSingleTerm _ _ (CASPoly [CASTerm (CASInteger c) mono]) = pure (c, 1, mono)+casSingleTerm _ _ (CASInteger n) = pure (n, 1, [])  -- Integer is a single term with empty monomial+casSingleTerm _ _ _ = mzero+casSingleTermM :: CASM -> p -> (Eql, Eql, Multiset (CASSymbolM, Eql))+casSingleTermM CASM _ = (Eql, Eql, Multiset (CASSymbolM, Eql))++-- | ValuePattern instance for CASM+instance ValuePattern CASM CASValue where+  value e () CASM v = if e == v then pure () else mzero++-- | ValuePattern instance for CASSymbolM+instance ValuePattern CASSymbolM SymbolExpr where+  value e () CASSymbolM v = if e == v then pure () else mzero
− hs-src/Language/Egison/Math/Expr.hs
@@ -1,360 +0,0 @@-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PatternSynonyms       #-}-{-# LANGUAGE QuasiQuotes           #-}--{- |-Module      : Language.Egison.Math.Expr-Licence     : MIT--This module defines the internal representation of mathematic objects such as-polynominals, and some useful patterns.--}--module Language.Egison.Math.Expr-    ( ScalarData (..)-    , PolyExpr (..)-    , TermExpr (..)-    , Monomial-    , SymbolExpr (..)-    , Printable (..)-    , pattern ZeroExpr-    , pattern SingleSymbol-    , pattern SingleTerm-    , ScalarM (..)-    , TermM (..)-    , SymbolM (..)-    , term-    , termM-    , symbol-    , symbolM-    , func-    , funcM-    , apply1-    , apply1M-    , apply2-    , apply2M-    , apply3-    , apply3M-    , apply4-    , apply4M-    , quote-    , negQuote-    , negQuoteM-    , quoteFunction-    , quoteFunctionM-    , equalMonomial-    , equalMonomialM-    , zero-    , zeroM-    , singleTerm-    , singleTermM-    , mathScalarMult-    , mathNegate-    , makeApplyExpr-    ) where--import           Data.List             (intercalate)-import           Prelude               hiding (foldr, mappend, mconcat)--import           Control.Egison-import           Control.Monad         (MonadPlus (..))--import           Language.Egison.IExpr (Index (..))-import {-# SOURCE #-} Language.Egison.Data (WHNFData, prettyFunctionName)------- Data------data ScalarData-  = Div PolyExpr PolyExpr- deriving Eq--newtype PolyExpr-  = Plus [TermExpr]--data TermExpr-  = Term Integer Monomial---- We choose the definition 'monomials' without its coefficients.--- ex. 2 x^2 y^3 is *not* a monomial. x^2 t^3 is a monomial.-type Monomial = [(SymbolExpr, Integer)]--data SymbolExpr-  = Symbol Id String [Index ScalarData]-  | Apply1 ScalarData ScalarData-  | Apply2 ScalarData ScalarData ScalarData-  | Apply3 ScalarData ScalarData ScalarData ScalarData-  | Apply4 ScalarData ScalarData ScalarData ScalarData ScalarData-  | Quote ScalarData                     -- For backtick quote: `expr-  | QuoteFunction WHNFData              -- For single quote on functions: 'func-  | FunctionData ScalarData [ScalarData] -- fnname args---- Manual Eq instance (QuoteFunction comparison always returns False)-instance Eq SymbolExpr where-  Symbol id1 s1 js1 == Symbol id2 s2 js2 = id1 == id2 && s1 == s2 && js1 == js2-  Apply1 f1 a1 == Apply1 f2 a2 = f1 == f2 && a1 == a2-  Apply2 f1 a1 b1 == Apply2 f2 a2 b2 = f1 == f2 && a1 == a2 && b1 == b2-  Apply3 f1 a1 b1 c1 == Apply3 f2 a2 b2 c2 = f1 == f2 && a1 == a2 && b1 == b2 && c1 == c2-  Apply4 f1 a1 b1 c1 d1 == Apply4 f2 a2 b2 c2 d2 = f1 == f2 && a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2-  Quote m1 == Quote m2 = m1 == m2-  QuoteFunction whnf1 == QuoteFunction whnf2 = -    case (prettyFunctionName whnf1, prettyFunctionName whnf2) of-      (Just n1, Just n2) -> n1 == n2-      _ -> False  -- Anonymous functions are never equal-  FunctionData n1 k1 == FunctionData n2 k2 = n1 == n2 && k1 == k2-  _ == _ = False---- Helper function to create Apply constructors based on argument count-makeApplyExpr :: ScalarData -> [ScalarData] -> SymbolExpr-makeApplyExpr fn [a1] = Apply1 fn a1-makeApplyExpr fn [a1, a2] = Apply2 fn a1 a2-makeApplyExpr fn [a1, a2, a3] = Apply3 fn a1 a2 a3-makeApplyExpr fn [a1, a2, a3, a4] = Apply4 fn a1 a2 a3 a4-makeApplyExpr _ _ = error "makeApplyExpr: unsupported number of arguments (must be 1-4)"--type Id = String---- Matchers--data ScalarM = ScalarM-instance Matcher ScalarM ScalarData--data TermM = TermM-instance Matcher TermM TermExpr--data SymbolM = SymbolM-instance Matcher SymbolM SymbolExpr--term :: Pattern (PP Integer, PP Monomial) TermM TermExpr (Integer, Monomial)-term _ _ (Term a mono) = pure (a, mono)-termM :: TermM -> TermExpr -> (Eql, Multiset (SymbolM, Eql))-termM TermM _ = (Eql, Multiset (SymbolM, Eql))--symbol :: Pattern (PP String) SymbolM SymbolExpr String-symbol _ _ (Symbol _ name []) = pure name-symbol _ _ _                  = mzero-symbolM :: SymbolM -> p -> Eql-symbolM SymbolM _ = Eql--func :: Pattern (PP ScalarData, PP [ScalarData])-                SymbolM SymbolExpr (ScalarData, [ScalarData])-func _ _ (FunctionData name args) = pure (name, args)-func _ _ _                        = mzero-funcM :: SymbolM -> SymbolExpr -> (ScalarM, List ScalarM)-funcM SymbolM _ = (ScalarM, List ScalarM)--apply1 :: Pattern (PP String, PP WHNFData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData)-apply1 _ _ (Apply1 (SingleSymbol (QuoteFunction fnWhnf)) a1) =-  case prettyFunctionName fnWhnf of-    Just fn -> pure (fn, fnWhnf, a1)-    Nothing -> mzero-apply1 _ _ _ = mzero-apply1M :: SymbolM -> p -> (Eql, Something, ScalarM)-apply1M SymbolM _ = (Eql, Something, ScalarM)--apply2 :: Pattern (PP String, PP WHNFData, PP ScalarData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData, ScalarData)-apply2 _ _ (Apply2 (SingleSymbol (QuoteFunction fnWhnf)) a1 a2) =-  case prettyFunctionName fnWhnf of-    Just fn -> pure (fn, fnWhnf, a1, a2)-    Nothing -> mzero-apply2 _ _ _ = mzero-apply2M :: SymbolM -> p -> (Eql, Something, ScalarM, ScalarM)-apply2M SymbolM _ = (Eql, Something, ScalarM, ScalarM)--apply3 :: Pattern (PP String, PP WHNFData, PP ScalarData, PP ScalarData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData, ScalarData, ScalarData)-apply3 _ _ (Apply3 (SingleSymbol (QuoteFunction fnWhnf)) a1 a2 a3) =-  case prettyFunctionName fnWhnf of-    Just fn -> pure (fn, fnWhnf, a1, a2, a3)-    Nothing -> mzero-apply3 _ _ _ = mzero-apply3M :: SymbolM -> p -> (Eql, Something, ScalarM, ScalarM, ScalarM)-apply3M SymbolM _ = (Eql, Something, ScalarM, ScalarM, ScalarM)--apply4 :: Pattern (PP String, PP WHNFData, PP ScalarData, PP ScalarData, PP ScalarData, PP ScalarData) SymbolM SymbolExpr (String, WHNFData, ScalarData, ScalarData, ScalarData, ScalarData)-apply4 _ _ (Apply4 (SingleSymbol (QuoteFunction fnWhnf)) a1 a2 a3 a4) =-  case prettyFunctionName fnWhnf of-    Just fn -> pure (fn, fnWhnf, a1, a2, a3, a4)-    Nothing -> mzero-apply4 _ _ _ = mzero-apply4M :: SymbolM -> p -> (Eql, Something, ScalarM, ScalarM, ScalarM, ScalarM)-apply4M SymbolM _ = (Eql, Something, ScalarM, ScalarM, ScalarM, ScalarM)--quote :: Pattern (PP ScalarData) SymbolM SymbolExpr ScalarData-quote _ _ (Quote m) = pure m-quote _ _ _         = mzero--negQuote :: Pattern (PP ScalarData) SymbolM SymbolExpr ScalarData-negQuote _ _ (Quote m) = pure (mathNegate m)-negQuote _ _ _         = mzero-negQuoteM :: SymbolM -> p -> ScalarM-negQuoteM SymbolM _ = ScalarM--quoteFunction :: Pattern (PP String, PP WHNFData) SymbolM SymbolExpr (String, WHNFData)-quoteFunction _ _ (QuoteFunction whnf) = case prettyFunctionName whnf of-  Just name -> pure (name, whnf)-  Nothing   -> mzero-quoteFunction _ _ _ = mzero-quoteFunctionM :: SymbolM -> p -> Eql-quoteFunctionM SymbolM _ = Eql--equalMonomial :: Pattern (PP Integer, PP Monomial) (Multiset (SymbolM, Eql)) Monomial (Integer, Monomial)-equalMonomial (_, VP xs) _ ys = case isEqualMonomial xs ys of-                                  Just sgn -> pure (sgn, xs)-                                  Nothing  -> mzero-equalMonomial _ _ _ = mzero-equalMonomialM :: Multiset (SymbolM, Eql) -> p -> (Eql, Multiset (SymbolM, Eql))-equalMonomialM (Multiset (SymbolM, Eql)) _ = (Eql, Multiset (SymbolM, Eql))--zero :: Pattern () ScalarM ScalarData ()-zero _ _ (Div (Plus []) _) = pure ()-zero _ _ _                 = mzero-zeroM :: ScalarM -> p -> ()-zeroM ScalarM _ = ()--singleTerm :: Pattern (PP Integer, PP Integer, PP Monomial) ScalarM ScalarData (Integer, Integer, Monomial)-singleTerm _ _ (Div (Plus [Term c mono]) (Plus [Term c2 []])) = pure (c, c2, mono)-singleTerm _ _ _                                              = mzero-singleTermM :: ScalarM -> p -> (Eql, Eql, Multiset (SymbolM, Eql))-singleTermM ScalarM _ = (Eql, Eql, Multiset (SymbolM, Eql))---instance ValuePattern ScalarM ScalarData where-  value e () ScalarM v = if e == v then pure () else mzero--instance ValuePattern SymbolM SymbolExpr where-  value e () SymbolM v = if e == v then pure () else mzero---pattern ZeroExpr :: ScalarData-pattern ZeroExpr = (Div (Plus []) (Plus [Term 1 []]))--pattern SingleSymbol :: SymbolExpr -> ScalarData-pattern SingleSymbol sym = Div (Plus [Term 1 [(sym, 1)]]) (Plus [Term 1 []])---- Product of a coefficient and a monomial-pattern SingleTerm :: Integer -> Monomial -> ScalarData-pattern SingleTerm coeff mono = Div (Plus [Term coeff mono]) (Plus [Term 1 []])--instance Eq PolyExpr where-  Plus xs == Plus ys =-    match dfs ys (Multiset Eql)-      [ [mc| #xs -> True |]-      , [mc| _   -> False |] ]--instance Eq TermExpr where-  Term a xs == Term b ys-    | a == b    = isEqualMonomial xs ys == Just 1-    | a == -b   = isEqualMonomial xs ys == Just (-1)-    | otherwise = False--isEqualMonomial :: Monomial -> Monomial -> Maybe Integer-isEqualMonomial xs ys =-  match dfs (xs, ys) (Multiset (SymbolM, Eql), Multiset (SymbolM, Eql))-    [ [mc| ((quote $s, $n) : $xss, (negQuote #s, #n) : $yss) ->-             case isEqualMonomial xss yss of-               Nothing -> Nothing-               Just sgn -> return (if even n then sgn else - sgn) |]-    , [mc| (($x, $n) : $xss, (#x, #n) : $yss) -> isEqualMonomial xss yss |]-    , [mc| ([], []) -> return 1 |]-    , [mc| _ -> Nothing |]-    ]-------  Arithmetic operations-----mathScalarMult :: Integer -> ScalarData -> ScalarData-mathScalarMult c (Div m n) = Div (f c m) n-  where-    f c (Plus ts) = Plus (map (\(Term a xs) -> Term (c * a) xs) ts)--mathNegate :: ScalarData -> ScalarData-mathNegate = mathScalarMult (-1)------- Pretty printing-----class Printable a where-  isAtom :: a -> Bool-  pretty :: a -> String--pretty' :: Printable a => a -> String-pretty' e | isAtom e = pretty e-pretty' e            = "(" ++ pretty e ++ ")"--instance Printable ScalarData where-  isAtom (Div p (Plus [Term 1 []])) = isAtom p-  isAtom _                          = False--  pretty (Div p1 (Plus [Term 1 []])) = pretty p1-  pretty (Div p1 p2)                 = pretty'' p1 ++ " / " ++ pretty' p2-    where-      pretty'' :: PolyExpr -> String-      pretty'' p@(Plus [_]) = pretty p-      pretty'' p            = "(" ++ pretty p ++ ")"--instance Printable PolyExpr where-  isAtom (Plus [])           = True-  isAtom (Plus [Term _ []])  = True-  isAtom (Plus [Term 1 [_]]) = True-  isAtom _                   = False--  pretty (Plus []) = "0"-  pretty (Plus (t:ts)) = pretty t ++ concatMap withSign ts-    where-      withSign (Term a xs) | a < 0 = " - " ++ pretty (Term (- a) xs)-      withSign t                   = " + " ++ pretty t--instance Printable SymbolExpr where-  isAtom Symbol{}        = True-  isAtom Quote{}         = True-  isAtom QuoteFunction{} = True-  isAtom _               = False--  pretty (Symbol _ (':':':':':':_) []) = "#"-  pretty (Symbol _ s [])               = s-  pretty (Symbol _ s js)               = s ++ concatMap show js-  pretty (Apply1 fn a1)                = unwords (map pretty' [fn, a1])-  pretty (Apply2 fn a1 a2)             = unwords (map pretty' [fn, a1, a2])-  pretty (Apply3 fn a1 a2 a3)          = unwords (map pretty' [fn, a1, a2, a3])-  pretty (Apply4 fn a1 a2 a3 a4)       = unwords (map pretty' [fn, a1, a2, a3, a4])-  pretty (Quote mExprs)                = "`" ++ pretty' mExprs-  pretty (QuoteFunction whnf)          = "'" ++ maybe "<function>" id (prettyFunctionName whnf)-  pretty (FunctionData name args)      = unwords (pretty name : map pretty' args)--instance Printable TermExpr where-  isAtom (Term _ [])  = True-  isAtom (Term 1 [_]) = True-  isAtom _            = False--  pretty (Term a [])    = show a-  pretty (Term 1 xs)    = intercalate " * " (map prettyPoweredSymbol xs)-  pretty (Term (-1) xs) = "- " ++ intercalate " * " (map prettyPoweredSymbol xs)-  pretty (Term a xs)    = intercalate " * " (show a : map prettyPoweredSymbol xs)--prettyPoweredSymbol :: (SymbolExpr, Integer) -> String-prettyPoweredSymbol (x, 1) = show x-prettyPoweredSymbol (x, n) = pretty' x ++ "^" ++ show n--instance Show ScalarData where-  show = pretty--instance Show PolyExpr where-  show = pretty--instance Show TermExpr where-  show = pretty--instance Show SymbolExpr where-  show = pretty--instance {-# OVERLAPPING #-} Show (Index ScalarData) where-  show (Sup i)    = "~" ++ pretty' i-  show (Sub i)    = "_" ++ pretty' i-  show (SupSub i) = "~_" ++ pretty' i-  show (DF _ _)   = ""-  show (User i)   = "|" ++ pretty' i
− hs-src/Language/Egison/Math/Normalize.hs
@@ -1,128 +0,0 @@-{-# LANGUAGE QuasiQuotes #-}--{- |-Module      : Language.Egison.Math.Expr-Licence     : MIT--This module implements the normalization of polynomials. Normalization rules-for particular mathematical functions (such as sqrt and sin/cos) are defined-in Rewrite.hs.--}--module Language.Egison.Math.Normalize-  ( mathNormalize'-  , termsGcd-  , mathDivideTerm-  ) where--import           Control.Egison--import           Language.Egison.Math.Expr---mathNormalize' :: ScalarData -> ScalarData-mathNormalize' = mathDivide . mathRemoveZero . mathFold . mathRemoveZeroSymbol--termsGcd :: [TermExpr] -> TermExpr-termsGcd ts@(_:_) =-  foldl1 (\(Term a xs) (Term b ys) -> Term (gcd a b) (monoGcd xs ys)) ts- where-  monoGcd :: Monomial -> Monomial -> Monomial-  monoGcd [] _ = []-  monoGcd ((x, n):xs) ys =-    case f (x, n) ys of-      (_, 0) -> monoGcd xs ys-      (z, m) -> (z, m) : monoGcd xs ys--  f :: (SymbolExpr, Integer) -> Monomial -> (SymbolExpr, Integer)-  f (x, _) [] = (x, 0)-  f (Quote x, n) ((Quote y, m):ys)-    | x == y            = (Quote x, min n m)-    | x == mathNegate y = (Quote x, min n m)-    | otherwise         = f (Quote x, n) ys-  f (x, n) ((y, m):ys)-    | x == y    = (x, min n m)-    | otherwise = f (x, n) ys--mathDivide :: ScalarData -> ScalarData-mathDivide mExpr@(Div (Plus _) (Plus [])) = mExpr-mathDivide mExpr@(Div (Plus []) (Plus _)) = mExpr-mathDivide (Div (Plus ts1) (Plus ts2)) =-  let z@(Term c zs) = termsGcd (ts1 ++ ts2) in-  case ts2 of-    [Term a _] | a < 0 -> Div (Plus (map (`mathDivideTerm` Term (-c) zs) ts1))-                              (Plus (map (`mathDivideTerm` Term (-c) zs) ts2))-    _                  -> Div (Plus (map (`mathDivideTerm` z) ts1))-                              (Plus (map (`mathDivideTerm` z) ts2))--mathDivideTerm :: TermExpr -> TermExpr -> TermExpr-mathDivideTerm (Term a xs) (Term b ys) =-  let (sgn, zs) = divMonomial xs ys in-  Term (sgn * div a b) zs- where-  divMonomial :: Monomial -> Monomial -> (Integer, Monomial)-  divMonomial xs [] = (1, xs)-  divMonomial xs ((y, m):ys) =-    match dfs (y, xs) (SymbolM, Multiset (SymbolM, Eql))-      -- Because we've applied |mathFold|, we can only divide the first matching monomial-      [ [mc| (quote $s, ($x & negQuote #s, $n) : $xss) ->-               let (sgn, xs') = divMonomial xss ys in-               let sgn' = if even m then 1 else -1 in-               if n == m then (sgn * sgn', xs')-                         else (sgn * sgn', (x, n - m) : xs') |]-      , [mc| (_, (#y, $n) : $xss) ->-               let (sgn, xs') = divMonomial xss ys in-               if n == m then (sgn, xs') else (sgn, (y, n - m) : xs') |]-      , [mc| _ -> divMonomial xs ys |]-      ]--mathRemoveZeroSymbol :: ScalarData -> ScalarData-mathRemoveZeroSymbol (Div (Plus ts1) (Plus ts2)) =-  let ts1' = map (\(Term a xs) -> Term a (filter p xs)) ts1-      ts2' = map (\(Term a xs) -> Term a (filter p xs)) ts2-   in Div (Plus ts1') (Plus ts2')-  where-    p (_, 0) = False-    p _      = True--mathRemoveZero :: ScalarData -> ScalarData-mathRemoveZero (Div (Plus ts1) (Plus ts2)) =-  let ts1' = filter (\(Term a _) -> a /= 0) ts1 in-  let ts2' = filter (\(Term a _) -> a /= 0) ts2 in-    case ts1' of-      [] -> Div (Plus []) (Plus [Term 1 []])-      _  -> Div (Plus ts1') (Plus ts2')--mathFold :: ScalarData -> ScalarData-mathFold = mathTermFold . mathSymbolFold---- x^2 y x -> x^3 y-mathSymbolFold :: ScalarData -> ScalarData-mathSymbolFold (Div (Plus ts1) (Plus ts2)) = Div (Plus (map f ts1)) (Plus (map f ts2))- where-  f :: TermExpr -> TermExpr-  f (Term a xs) =-    let (sgn, ys) = g xs in Term (sgn * a) ys-  g :: Monomial -> (Integer, Monomial)-  g [] = (1, [])-  g ((x, m):xs) =-    match dfs (x, xs) (SymbolM, Multiset (SymbolM, Eql))-      [ [mc| (quote $s, (negQuote #s, $n) : $xs) ->-               let (sgn, ys) = g ((x, m + n) : xs) in-               if even n then (sgn, ys) else (- sgn, ys) |]-      , [mc| (_, (#x, $n) : $xs) -> g ((x, m + n) : xs) |]-      , [mc| _ -> let (sgn', ys) = g xs in (sgn', (x, m):ys) |]-      ]---- x^2 y + x^2 y -> 2 x^2 y-mathTermFold :: ScalarData -> ScalarData-mathTermFold (Div (Plus ts1) (Plus ts2)) = Div (Plus (f ts1)) (Plus (f ts2))- where-  f :: [TermExpr] -> [TermExpr]-  f [] = []-  f (t:ts) =-    match dfs (t, ts) (TermM, Multiset TermM)-      [ [mc| (term $a $xs, term $b (equalMonomial $sgn #xs) : $tss) ->-               f (Term (sgn * a + b) xs : tss) |]-      , [mc| _ -> t : f ts |]-      ]
hs-src/Language/Egison/Math/Rewrite.hs view
@@ -4,264 +4,377 @@ Module      : Language.Egison.Math.Rewrite Licence     : MIT -This module implements rewrite rules for common mathematical functions.+Residual mathematical rewrite rules kept in Haskell.++Remaining functions:+  - casRewriteDd : merge polynomial terms with same FunctionData factor.+    The equivalent declare-rule poly pattern is too expensive for complex+    differential-form samples (e.g. riemann-curvature-tensor-of-S2xS3).+  - casRewriteSqrt : sqrt power reduction and sqrt pair merging.+    Ported back from declare rule (G6 of design/cas-simplification.md):+    the Egison-level term rules paid a pattern-match attempt on every+    term of every sqrt-carrying value per normalization, making+    arithmetic on such values ~20x slower (thurston.egi's bottleneck).+  - casRewriteExp : exp power reduction and exp product merging+    ((exp a)^n -> exp (n a), exp a * exp b -> exp (a+b)), ported back+    for the same reason (an identical 60-operation fold on+    exp-carrying operands measured 20.7s under the declare rules).+    The value rules (exp 0 = 1, exp 1 = e, exp (n i pi) = (-1)^n)+    stay in the library.++Migrated to declare rule (and removed):+  - casRewriteI, casRewriteW, casRewriteLog+  - casRewritePower, casRewriteRt, casRewriteRtu -}  module Language.Egison.Math.Rewrite-  ( rewriteSymbol+  ( casRewriteSymbol   ) where  import           Control.Egison -import           Language.Egison.Math.Arith-import           Language.Egison.Math.Expr-import           Language.Egison.Math.Normalize-import {-# SOURCE #-} Language.Egison.Data (WHNFData)+import           Language.Egison.Math.CAS +-- | Apply rewrite rules to a CASValue.+casRewriteSymbol :: CASValue -> CASValue+casRewriteSymbol = casRewriteDd . casRewriteSqrt . casRewriteExp -rewriteSymbol :: ScalarData -> ScalarData-rewriteSymbol =-  foldl1 (\acc f -> f . acc)-    [ rewriteI-    , rewriteW-    , rewriteLog---    , rewriteSinCos-    , rewriteExp-    , rewritePower-    , rewriteSqrt-    , rewriteRt-    , rewriteRtu-    , rewriteDd-    ]+-- | Rewrite sqrt factors of every term (top level of the value, the+-- same scope as casRewriteDd; inner values were normalized when they+-- were constructed):+--+--   1. Power reduction: (sqrt a)^n with |n| >= 2 becomes+--      a^q * (sqrt a)^r with q = quot n 2 and r = n - 2q in {-1,0,1}+--      (matching the power-evaluation path's normal form).+--   2. Pair merge: sqrt a * sqrt b (both with exponent 1) merges to+--      sqrt (a*b), and the square part of a single-term product is+--      extracted: sqrt 2 * sqrt 8 = sqrt 16 = 4,+--      sqrt (2x) * sqrt (2y) = 2 * sqrt (x y),+--      sqrt x * sqrt (x y^2) = x y.  Multi-term (polynomial) products+--      stay under the sqrt: sqrt (x+1) * sqrt (x-1) = sqrt (x^2-1).+--+-- Fast path: values none of whose terms need sqrt work (see+-- termNeedsSqrtWork) are returned unchanged, without any rebuilding.+casRewriteSqrt :: CASValue -> CASValue+casRewriteSqrt = go (100 :: Int)+ where+  -- Iterate to a fixpoint (like the old applyRuleFix): one rewrite+  -- pass can create new reducible shapes -- merging produces products+  -- whose casMult combines equal atoms into powers, and content+  -- splitting produces pairs that reproduce themselves identically+  -- (sqrt 2 * sqrt(-sqrt 5 - 5) merges and re-splits to the same+  -- pair, which the equality check turns into a fixpoint).+  go 0 v = v+  go fuel v =+    let v' = rewriteSqrtOnce v+    in if v' == v then v else go (fuel - 1) v' -mapTerms :: (TermExpr -> TermExpr) -> ScalarData -> ScalarData-mapTerms f (Div (Plus ts1) (Plus ts2)) =-  Div (Plus (map f ts1)) (Plus (map f ts2))+rewriteSqrtOnce :: CASValue -> CASValue+rewriteSqrtOnce (CASFrac num denom)+  | valueNeedsSqrtWork num || valueNeedsSqrtWork denom =+      casDivide (rewriteSqrtPart num) (rewriteSqrtPart denom)+rewriteSqrtOnce v@(CASPoly _)+  | valueNeedsSqrtWork v = rewriteSqrtPart v+rewriteSqrtOnce v = v -mapTerms' :: (TermExpr -> ScalarData) -> ScalarData -> ScalarData-mapTerms' f (Div (Plus ts1) (Plus ts2)) =-  mathDiv (foldl mathPlus (Div (Plus []) (Plus [Term 1 []])) (map f ts1)) (foldl mathPlus (Div (Plus []) (Plus [Term 1 []])) (map f ts2))+-- | Fast path: a term needs sqrt work if it has a sqrt factor with+-- |exponent| >= 2, at least two sqrt factors with exponent 1, or a+-- nested value (an application argument, a quoted expression, or a+-- level-4 coefficient) that needs work itself -- the old declare-rule+-- versions recursed into those via mapTermAll, and nested-radical+-- reductions such as sqrt 5 * sqrt(-5-2 sqrt 5) * sqrt(-5+2 sqrt 5) = 5+-- depend on it.  Terms with a lone top-level (sqrt a)^(+-1) factor and+-- quiet insides -- the common shape in curvature-style values -- are+-- skipped without any rebuilding, which is the point of the port.+termNeedsSqrtWork :: CASTerm -> Bool+termNeedsSqrtWork (CASTerm c mono) = go 0 mono || valueNeedsSqrtWork c+ where+  go :: Int -> Monomial -> Bool+  go ones ((sym, n) : rest) = case sqrtRadicand sym of+    Just _+      | abs n >= 2       -> True+      | n == 1 && ones >= 1 -> True+      | n == 1           -> symNeedsSqrtWork sym || go 1 rest+      | otherwise        -> symNeedsSqrtWork sym || go ones rest+    Nothing              -> symNeedsSqrtWork sym || go ones rest+  go _ [] = False -mapPolys :: (PolyExpr -> PolyExpr) -> ScalarData -> ScalarData-mapPolys f (Div p1 p2) = Div (f p1) (f p2)+symNeedsSqrtWork :: SymbolExpr -> Bool+symNeedsSqrtWork = symNeedsWorkWith valueNeedsSqrtWork -rewriteI :: ScalarData -> ScalarData-rewriteI = mapTerms f- where-  f term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (symbol #"i", $k) : $xss ->-              if even k-                then Term (a * (-1) ^ (quot k 2)) xss-                else Term (a * (-1) ^ (quot k 2)) ((Symbol "" "i" [], 1) : xss) |]-      , [mc| _ -> term |]-      ]+valueNeedsSqrtWork :: CASValue -> Bool+valueNeedsSqrtWork (CASFrac n d) = valueNeedsSqrtWork n || valueNeedsSqrtWork d+valueNeedsSqrtWork (CASPoly ts)  = any termNeedsSqrtWork ts+valueNeedsSqrtWork _             = False -rewriteW :: ScalarData -> ScalarData-rewriteW = mapPolys g . mapTerms f- where-  f term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (symbol #"w", $k & ?(>= 3)) : $xss ->-               Term a ((Symbol "" "w" [], k `mod` 3) : xss) |]-      , [mc| _ -> term |]-      ]-  g poly@(Plus ts) =-    match dfs ts (Multiset TermM)-      [ [mc| term $a ((symbol #"w", #2) : $mr) :-             term $b ((symbol #"w", #1) : #mr) : $pr ->-               g (Plus (Term (-a) mr :-                        Term (b - a) ((Symbol "" "w" [], 1) : mr) : pr)) |]-      , [mc| _ -> poly |]-      ]+-- | Rewrite the terms that need it and re-combine.  Combination goes+-- through casPlus/casDivide (not a raw CASPoly rebuild) because a+-- negative radicand power over a polynomial radicand produces a+-- genuine fraction: (sqrt (x+1))^-2 = 1/(x+1).+rewriteSqrtPart :: CASValue -> CASValue+rewriteSqrtPart (CASPoly ts) =+  let changed   = filter termNeedsSqrtWork ts+      unchanged = filter (not . termNeedsSqrtWork) ts+  in foldl casPlus (casNormalizePoly unchanged) (map rewriteSqrtTerm changed)+rewriteSqrtPart v = v -rewriteLog :: ScalarData -> ScalarData-rewriteLog = mapTerms f- where-  f term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"log" _ zero, _) : _ -> Term 0 [] |]-      , [mc| (apply1 #"log" _ (singleTerm _ #1 [(symbol #"e", $n)]), _) : $xss ->-              Term (n * a) xss |]-      , [mc| _ -> term |]-      ]+-- | The argument of a unary application factor of the named function.+applyArg1 :: String -> SymbolExpr -> Maybe CASValue+applyArg1 name (Apply1 fh a) = case fh of+  CASFactor (QuoteFunction w)+    | prettyFunctionName w == Just name -> Just a+  CASPoly [CASTerm (CASInteger 1) [(QuoteFunction w, 1)]]+    | prettyFunctionName w == Just name -> Just a+  _ -> Nothing+applyArg1 _ _ = Nothing -makeApply :: WHNFData -> [ScalarData] -> SymbolExpr-makeApply f args =-  makeApplyExpr (SingleSymbol (QuoteFunction f)) args+-- | The radicand of a sqrt application factor, if it is one.+sqrtRadicand :: SymbolExpr -> Maybe CASValue+sqrtRadicand = applyArg1 "sqrt" -rewriteExp :: ScalarData -> ScalarData-rewriteExp = mapTerms f- where-  f term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"exp" _ zero, _) : $xss ->-               f (Term a xss) |]-      , [mc| (apply1 #"exp" _ (singleTerm #1 #1 []), _) : $xss ->-               f (Term a ((Symbol "" "e" [], 1) : xss)) |]-      , [mc| (apply1 #"exp" _ (singleTerm $n #1 [(symbol #"i", #1), (symbol #"π", #1)]), _) : $xss ->-               f (Term ((-1) ^ n * a) xss) |]-      , [mc| (apply1 #"exp" $expWhnf $x, $n & ?(>= 2)) : $xss ->-               f (Term a ((makeApply expWhnf [mathScalarMult n x], 1) : xss)) |]-      , [mc| (apply1 #"exp" $expWhnf $x, #1) : (apply1 #"exp" _ $y, #1) : $xss ->-               f (Term a ((makeApply expWhnf [mathPlus x y], 1) : xss)) |]-      , [mc| _ -> term |]-      ]+-- | Generic traversals shared by the factor rewriters: does any+-- nested value (application argument, quoted expression) satisfy the+-- check / rewrite every nested value.  Each rewriter recurses only+-- into itself, matching the old per-rule mapTermAll recursion.+symNeedsWorkWith :: (CASValue -> Bool) -> SymbolExpr -> Bool+symNeedsWorkWith p (Apply1 _ a)       = p a+symNeedsWorkWith p (Apply2 _ a b)     = p a || p b+symNeedsWorkWith p (Apply3 _ a b c)   = p a || p b || p c+symNeedsWorkWith p (Apply4 _ a b c d) = p a || p b || p c || p d+symNeedsWorkWith p (Quote q)          = p q+symNeedsWorkWith _ _                  = False -rewritePower :: ScalarData -> ScalarData-rewritePower = mapTerms f- where-  f term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"^" _ (singleTerm #1 #1 []), _) : $xss -> f (Term a xss) |]-      , [mc| (apply2 #"^" $powerWhnf $x $y, $n & ?(>= 2)) : $xss ->-               f (Term a ((makeApply powerWhnf [x, mathScalarMult n y], 1) : xss)) |]-      , [mc| (apply2 #"^" $powerWhnf $x $y, #1) : (apply2 #"^" _ #x $z, #1) : $xss ->-               f (Term a ((makeApply powerWhnf [x, mathPlus y z], 1) : xss)) |]-      , [mc| _ -> term |]-      ]+rewriteInsideSymWith :: (CASValue -> CASValue) -> SymbolExpr -> SymbolExpr+rewriteInsideSymWith f sym = case sym of+  Apply1 g a       -> Apply1 g (f a)+  Apply2 g a b     -> Apply2 g (f a) (f b)+  Apply3 g a b c   -> Apply3 g (f a) (f b) (f c)+  Apply4 g a b c d -> Apply4 g (f a) (f b) (f c) (f d)+  Quote q          -> Quote (f q)+  _                -> sym -rewriteSinCos :: ScalarData -> ScalarData-rewriteSinCos = h . mapTerms (g . f)+-- | Rewrite the sqrt factors of one term.  Returns a CASValue because+-- extracted radicand powers can be polynomials.  Nested values+-- (application arguments, quoted expressions, level-4 coefficients)+-- are rewritten first, bottom-up, matching the old mapTermAll+-- recursion of the declare-rule versions.+rewriteSqrtTerm :: CASTerm -> CASValue+rewriteSqrtTerm (CASTerm c0 mono0) =+  let c    = casRewriteSqrt c0+      mono = [ (rewriteInsideSym sym, n) | (sym, n) <- mono0 ]+      -- 1. power reduction on every factor with |n| >= 2+      (powerOuts, mono1) = foldr powerStep ([], []) mono+      powerStep (sym, n) (outs, ms) = case sqrtRadicand sym of+        Just a | abs n >= 2 ->+          let q = n `quot` 2+              r = n - 2 * q+          in (casPower a q : outs, if r == 0 then ms else (sym, r) : ms)+        _ -> (outs, (sym, n) : ms)+      -- 2. pair merge on the remaining exponent-1 sqrt factors+      (sqrtOnes, others) = partitionSqrtOnes mono1+      base = CASPoly [CASTerm c others]+      merged = case sqrtOnes of+        ((sym0, _) : _ : _) ->+          let radicands = map snd sqrtOnes+              product'  = casRewriteSqrt (foldr1 casMult radicands)+              (outside, insides) = splitSquarePart product'+              sqrtAtom r = case sym0 of+                Apply1 fh _ -> CASPoly [CASTerm (CASInteger 1) [(Apply1 fh r, 1)]]+                _           -> error "rewriteSqrtTerm: non-Apply1 sqrt factor"+          in outside : map sqrtAtom insides+        [(sym0, _)] -> [CASPoly [CASTerm (CASInteger 1) [(sym0, 1)]]]+        []          -> []+  in foldl casMult base (powerOuts ++ merged)  where-  f term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"sin" _ zero, _) : _ -> Term 0 [] |]-      , [mc| (apply1 #"sin" _ (singleTerm _ #1 [(symbol #"π", #1)]), _) : _ ->-               Term 0 [] |]-      , [mc| (apply1 #"sin" _ (singleTerm $n #2 [(symbol #"π", #1)]), $m) : $xss ->-              Term (a * (-1) ^ (div (abs n - 1) 2) * m) xss |]-      , [mc| _ -> term |]-      ]-  g term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"cos" _ zero, _) : $xss -> Term a xss |]-      , [mc| (apply1 #"cos" _ (singleTerm _ #2 [(symbol #"π", #1)]), _) : _ ->-              Term 0 [] |]-      , [mc| (apply1 #"cos" _ (singleTerm $n #1 [(symbol #"π", #1)]), $m) : $xss ->-               Term (a * (-1) ^ (abs n * m)) xss |]-      , [mc| _ -> term |]-      ]-  h (Div poly1@(Plus ts1) poly2@(Plus ts2)) =-    match dfs (ts1, ts2) (Multiset TermM, Multiset TermM)-      [ [mc| ((term $a ((apply1 #"cos" $cosWhnf $x, #2) : $mr)) : (term $b ((apply1 #"sin" $sinWhnf #x, #2) : #mr)) : $pr, _) ->-              h (Div (Plus (Term a mr : Term (b - a) ((makeApply sinWhnf [x], 2) : mr) : pr)) poly2) |]-      , [mc| ((term $a ((apply1 #"cos" $cosWhnf $x, #2) : $mr)) : $pr1, (term _ ((apply1 #"sin" $sinWhnf #x, #2) : #mr)) : _) ->-              h (Div (Plus (Term a mr : Term (- a) ((makeApply sinWhnf [x], 2) : mr) : pr1)) poly2) |]-      , [mc| _ -> Div poly1 poly2 |]-      ]+  partitionSqrtOnes = foldr step ([], [])+   where+    step (sym, 1) (sq, rest) = case sqrtRadicand sym of+      Just a  -> ((sym, a) : sq, rest)+      Nothing -> (sq, (sym, 1) : rest)+    step f (sq, rest) = (sq, f : rest) --- Determine if a ScalarData is definitely negative--- Returns Just True if negative, Just False if non-negative, Nothing if unknown-isNegativeScalar :: ScalarData -> Maybe Bool-isNegativeScalar (Div (Plus terms) (Plus [Term d []]))-  | d > 0 = analyzeTerms terms-  | d < 0 = fmap not (analyzeTerms terms)- where-  analyzeTerms ts-    | all (\(Term a _) -> a < 0) ts = Just True-    | all (\(Term a _) -> a > 0) ts = Just False-    | otherwise =-      -- Two-term case: a + b*sqrt(n), compare a^2 with b^2*n-      match dfs ts (Multiset TermM)-        [ [mc| term $a [] :-               term $b ((apply1 #"sqrt" _ (singleTerm $n #1 []), #1) : []) :-               [] ->-                 if n > 0-                 then let lhs = a * a; rhs = b * b * n-                      in if lhs > rhs then Just (a < 0)-                         else if lhs < rhs then Just (b < 0)-                         else Just False-                 else Nothing |]-        , [mc| _ -> Nothing |]-        ]-isNegativeScalar _ = Nothing+  rewriteInsideSym = rewriteInsideSymWith casRewriteSqrt --- Find a pair of sqrts in a monomial whose product simplifies to a single term.--- Uses matchAll to enumerate all sqrt pairs, avoiding DFS ordering issues.--- We apply rewriteSqrt to the product because mathMult alone does not simplify--- sqrt(x)^2 to x, which is needed for products like (-5-2√5)*(-5+2√5).-findSqrtPairToMerge :: Monomial -> Maybe (WHNFData, ScalarData, Monomial, Integer)-findSqrtPairToMerge xs =-  case results of-    (r:_) -> Just r-    []    -> Nothing+-- | Split a merged radicand into (outside, inside radicands) with+-- value = outside^2 * product(insides), extracting square content and+-- canonicalizing the atom forms:+--+--   * single-term radicand: extract the square part of the integer+--     coefficient and the even exponents (sqrt 16 = 4,+--     sqrt (4 x^2 y) = 2 x sqrt y); the remainder stays as ONE atom.+--   * multi-term radicand: extract the integer content's square part,+--     and split the squarefree content off as its OWN integer sqrt+--     atom, leaving a content-free polynomial radicand.  This matches+--     the stable form the old declare-rule + lib-sqrt round trip+--     converged to (sqrt 5 * sqrt(-10 sqrt 5 - 50) =+--     5 * sqrt(-2 sqrt 5 - 10)); without it, algebraically related+--     atoms appear in several content forms and sums that should+--     cancel (5th roots of unity, mini-test 117) do not.+--+-- Anything unsupported stays entirely inside (value-safe).+splitSquarePart :: CASValue -> (CASValue, [CASValue])+splitSquarePart (CASPoly [CASTerm (CASInteger m) mono])+  | m >= 0 =+      let (s, m') = integerSquarePart m+          outsideFactors = [ (sym, k `div` 2) | (sym, k) <- mono, k `div` 2 /= 0 ]+          insideFactors  = [ (sym, k `mod` 2) | (sym, k) <- mono, k `mod` 2 /= 0 ]+          outside = CASPoly [CASTerm (CASInteger s) outsideFactors]+          inside  = casNormalizePoly [CASTerm (CASInteger m') insideFactors]+      in (outside, if inside == CASInteger 1 then [] else [inside])+splitSquarePart (CASInteger m)+  | m >= 0 =+      let (s, m') = integerSquarePart m+      in (CASInteger s, if m' == 1 then [] else [CASInteger m'])+splitSquarePart (CASPoly ts@(_ : _ : _))+  | Just coeffs <- mapM intCoeff ts+  , let g = foldr1 gcd (map abs coeffs)+  , g > 1 =+      let (s, g') = integerSquarePart g+          prim = casNormalizePoly+                   [ CASTerm (CASInteger (c `div` g)) mono | CASTerm (CASInteger c) mono <- ts ]+      in ( CASInteger s+         , (if g' == 1 then [] else [CASInteger g']) ++ [prim] )  where-  results =-    [ (whnf, simplified, xss, sign)-    | (whnf, x, y, xss) <- matchAll dfs xs (Multiset (SymbolM, Eql))-        [ [mc| (apply1 #"sqrt" $whnf $x, #1) :-               (apply1 #"sqrt" _ $y, #1) : $xss ->-                 (whnf, x, y, xss) |] ]-    , let simplified = rewriteSqrt (mathMult x y)-    , isSingleTermScalar simplified-    , let sign = case (isNegativeScalar x, isNegativeScalar y) of-                   (Just True, Just True) -> -1-                   _                      -> 1-    ]-  isSingleTermScalar (Div (Plus [_]) (Plus [_])) = True-  isSingleTermScalar _ = False+  intCoeff (CASTerm (CASInteger c) _) = Just c+  intCoeff _                          = Nothing+splitSquarePart v = (CASInteger 1, [v]) -rewriteSqrt :: ScalarData -> ScalarData-rewriteSqrt = mapTerms' f+-- | Largest s with s^2 dividing m (m >= 0), by trial division with a+-- divisor cap: square factors hiding behind primes above the cap are+-- left inside (value-safe, just less extraction).+integerSquarePart :: Integer -> (Integer, Integer)+integerSquarePart m0 = go m0 2  where-  f (Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"sqrt" $sqrtWhnf $x, ?(> 1) & $k) : $xss ->-               rewriteSqrt-                 (mathMult (SingleTerm a ((makeApply sqrtWhnf [x], k `mod` 2) : xss))-                           (mathPower x (div k 2))) |]-      , [mc| (apply1 #"sqrt" $sqrtWhnf (singleTerm $n #1 $x), #1) :-               (apply1 #"sqrt" _ (singleTerm $m #1 $y), #1) : $xss ->-             let d@(Term c z) = termsGcd [Term n x, Term m y]-                 Term n' x' = mathDivideTerm (Term n x) d-                 Term m' y' = mathDivideTerm (Term m y) d-                 in case (n' * m', Term n' x', Term m' y') of-                      (1, Term _ [], Term _ []) -> mathMult (SingleTerm c z) (SingleTerm a xss)-                      (_, _, _) -> mathMult (SingleTerm c z) (SingleTerm a ((makeApply sqrtWhnf [SingleTerm (n' * m') (x' ++ y')], 1) : xss)) |]-      , [mc| _ -> case findSqrtPairToMerge xs of-                    Just (whnf, product, remaining, sign) ->-                      rewriteSqrt (SingleTerm (sign * a) ((makeApply whnf [product], 1) : remaining))-                    Nothing -> SingleTerm a xs |]-      ]+  go :: Integer -> Integer -> (Integer, Integer)+  cap = 1000000+  go m p+    | p > cap || p * p > m = (1, m)+    | m `mod` p == 0 =+        let (e, m')   = strip m p 0+            (s, rest) = go m' (p + 1)+        in (p ^ (e `div` 2) * s, p ^ (e `mod` 2) * rest)+    | otherwise = go m (p + 1)+  strip :: Integer -> Integer -> Integer -> (Integer, Integer)+  strip m p e+    | m `mod` p == 0 = strip (m `div` p) p (e + 1)+    | otherwise      = (e, m) -rewriteRt :: ScalarData -> ScalarData-rewriteRt = mapTerms' f+-- | Rewrite exp factors of every term:+--+--   1. Power reduction: (exp a)^n with n /= 1 becomes exp (n a).+--   2. Product merge: all exp factors of a term merge into one,+--      exp a * exp b = exp (a+b); a zero total collapses to 1+--      (exp x * exp (-x) = 1).+--+-- One pass per term suffices (the merge is closed: it produces at+-- most one exp factor with exponent 1).  Nested values are rewritten+-- first, like casRewriteSqrt.  The value rules exp 0 = 1, exp 1 = e,+-- and exp (n i pi) = (-1)^n stay in the library and fire on the+-- merged atom as before.+casRewriteExp :: CASValue -> CASValue+casRewriteExp (CASFrac num denom)+  | valueNeedsExpWork num || valueNeedsExpWork denom =+      casDivide (rewriteExpPart num) (rewriteExpPart denom)+casRewriteExp v@(CASPoly _)+  | valueNeedsExpWork v = rewriteExpPart v+casRewriteExp v = v++expArg :: SymbolExpr -> Maybe CASValue+expArg = applyArg1 "exp"++-- | A term needs exp work if it has an exp factor with exponent /= 1,+-- at least two exp factors, or nested work.+termNeedsExpWork :: CASTerm -> Bool+termNeedsExpWork (CASTerm c mono) = go 0 mono || valueNeedsExpWork c  where-  f (Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply2 #"rt" _ (singleTerm $n #1 []) $x & $rtnx, ?(>= n) & $k) : $xss ->-               mathMult (SingleTerm a ((rtnx, k `mod` n) : xss))-                        (mathPower x (div k n)) |]-      , [mc| _ -> SingleTerm a xs |]-      ]+  go :: Int -> Monomial -> Bool+  go ones ((sym, n) : rest) = case expArg sym of+    Just _+      | n /= 1    -> True+      | ones >= 1 -> True+      | otherwise -> symNeedsWorkWith valueNeedsExpWork sym || go 1 rest+    Nothing       -> symNeedsWorkWith valueNeedsExpWork sym || go ones rest+  go _ [] = False -rewriteRtu :: ScalarData -> ScalarData-rewriteRtu = mapTerms' g . mapTerms f+valueNeedsExpWork :: CASValue -> Bool+valueNeedsExpWork (CASFrac n d) = valueNeedsExpWork n || valueNeedsExpWork d+valueNeedsExpWork (CASPoly ts)  = any termNeedsExpWork ts+valueNeedsExpWork _             = False++rewriteExpPart :: CASValue -> CASValue+rewriteExpPart (CASPoly ts) =+  let changed   = filter termNeedsExpWork ts+      unchanged = filter (not . termNeedsExpWork) ts+  in foldl casPlus (casNormalizePoly unchanged) (map rewriteExpTerm changed)+rewriteExpPart v = v++rewriteExpTerm :: CASTerm -> CASValue+rewriteExpTerm (CASTerm c0 mono0) =+  let c    = casRewriteExp c0+      mono = [ (rewriteInsideSymWith casRewriteExp sym, n) | (sym, n) <- mono0 ]+      (exps, others) = foldr step ([], []) mono+      step (sym, n) (es, ms) = case expArg sym of+        Just a  -> ((sym, casMult (CASInteger n) a) : es, ms)+        Nothing -> (es, (sym, n) : ms)+      base = CASPoly [CASTerm c others]+  in case exps of+       [] -> base+       ((sym0, _) : _) ->+         let total = foldr1 casPlus (map snd exps)+             atom = case sym0 of+               Apply1 fh _ -> CASPoly [CASTerm (CASInteger 1) [(Apply1 fh total, 1)]]+               _           -> error "rewriteExpTerm: non-Apply1 exp factor"+         in if casIsZero total+              then base+              else casMult base atom++-- | Rewrite dd (differential): merge polynomial terms whose monomial shares+-- the same FunctionData factor (same `g`, `args`, exponent, and rest of+-- monomial). Kept in Haskell because the equivalent+-- `declare rule auto poly` (multi-term, same-binding) is too expensive for+-- complex differential-form samples (e.g. riemann-curvature-tensor-of-S2xS3).+--+-- Fast path: if the value contains no FunctionData factor anywhere, the+-- pattern can't fire — return the value unchanged. This avoids the+-- per-mathNormalize-call overhead of `rewriteDdPoly`'s multi-term matcher+-- on the vast majority of values that have no `Function _ _` factors.+casRewriteDd :: CASValue -> CASValue+casRewriteDd v+  | not (casHasFunctionData v) = v+casRewriteDd (CASFrac num denom) =+  CASFrac (casNormalizePoly (rewriteDdPoly (extractTerms num)))+         (casNormalizePoly (rewriteDdPoly (extractTerms denom)))  where-  f term@(Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"rtu" _ (singleTerm $n #1 []) & $rtun, ?(>= n) & $k) : $r ->-               Term a ((rtun, k `mod` n) : r) |]-      , [mc| _ -> term |]-      ]-  g (Term a xs) =-    match dfs xs (Multiset (SymbolM, Eql))-      [ [mc| (apply1 #"rtu" _ (singleTerm $n #1 []) & $rtun, ?(== n - 1)) : $mr ->-               mathMult-                 (foldl mathMinus (SingleTerm (-1) []) (map (\k -> SingleTerm 1 [(rtun, k)]) [1..(n-2)]))-                 (g (Term a mr)) |]-      , [mc| _ -> SingleTerm a xs |]-      ]+  extractTerms (CASPoly ts) = ts+  extractTerms (CASInteger n) = [CASTerm (CASInteger n) []]+  extractTerms _ = []+casRewriteDd (CASPoly ts) = casNormalizePoly (rewriteDdPoly ts)+casRewriteDd v = v -rewriteDd :: ScalarData -> ScalarData-rewriteDd (Div (Plus p1) (Plus p2)) =-  Div (Plus (rewriteDdPoly p1)) (Plus (rewriteDdPoly p2))+-- | True if the CASValue contains any `FunctionData` factor anywhere in+-- its tree.+casHasFunctionData :: CASValue -> Bool+casHasFunctionData = goV  where-  rewriteDdPoly poly =-    match dfs poly (Multiset TermM)-      [ [mc| term $a (($f & func $g $args, $n) : $mr) :-               term $b ((func #g #args, #n) : #mr) : $pr ->-                 rewriteDdPoly (Term (a + b) ((f, n) : mr) : pr) |]-      , [mc| _ -> poly |]-      ]+  goV (CASInteger _)  = False+  goV (CASFactor sym) = goSym sym+  goV (CASPoly terms) = any goTerm terms+  goV (CASFrac n d)   = goV n || goV d+  goTerm (CASTerm coeff mono) = goV coeff || any (goSym . fst) mono+  goSym (FunctionData {})  = True+  goSym (Apply1 f a)        = goV f || goV a+  goSym (Apply2 f a b)      = goV f || goV a || goV b+  goSym (Apply3 f a b c)    = goV f || goV a || goV b || goV c+  goSym (Apply4 f a b c d)  = goV f || goV a || goV b || goV c || goV d+  goSym (Quote v)           = goV v+  goSym (Symbol _ _ _)      = False+  goSym (QuoteFunction _)   = False++rewriteDdPoly :: [CASTerm] -> [CASTerm]+rewriteDdPoly poly =+  match dfs poly (Multiset CASTermM)+    [ [mc| casTerm' $a (($f & casFunc $g $args, $n) : $mr) :+           casTerm' $b ((casFunc #g #args, #n) : #mr) : $pr ->+             rewriteDdPoly (CASTerm (casPlus a b) ((f, n) : mr) : pr) |]+    , [mc| _ -> poly |]+    ]
hs-src/Language/Egison/MathOutput.hs view
@@ -21,14 +21,14 @@ prettyMath lang val =   -- 'lang' is either "asciimath", "latex", "mathematica" or "maxima"   -- Other invalid options are rejected in Interpreter/egison.hs-  case showMathExpr lang (toMathExpr val) of+  case showMathValue lang (toMathValue val) of     "undefined" -> "undefined"     output      -> "#" ++ lang ++ "|" ++ output ++ "|#" -showMathExpr :: String -> MathExpr -> String-showMathExpr "asciimath"   = AsciiMath.showMathExpr-showMathExpr "latex"       = Latex.showMathExpr-showMathExpr "mathematica" = Mathematica.showMathExpr-showMathExpr "maxima"      = Maxima.showMathExpr-showMathExpr "haskell"     = show-showMathExpr _             = error "Unreachable"+showMathValue :: String -> MathValue -> String+showMathValue "asciimath"   = AsciiMath.showMathValue+showMathValue "latex"       = Latex.showMathValue+showMathValue "mathematica" = Mathematica.showMathValue+showMathValue "maxima"      = Maxima.showMathValue+showMathValue "haskell"     = show+showMathValue _             = error "Unreachable"
hs-src/Language/Egison/Parser/NonS.hs view
@@ -18,12 +18,13 @@        , lowerReservedWords        ) where +import           Control.Monad                  (guard) import           Control.Monad.State            (get, gets, put)  import           Data.Char                      (isAsciiUpper, isLetter) import           Data.Either                    (isRight) import           Data.Function                  (on)-import           Data.Functor                   (($>))+import           Data.Functor                   (($>), void) import           Data.List                      (groupBy, insertBy, sortOn) import           Data.Maybe                     (catMaybes, isJust, isNothing) import           Data.Text                      (pack)@@ -90,6 +91,12 @@       <|> LoadFile <$> (reserved "loadFile" >> stringLiteral)       <|> Execute  <$> (reserved "execute" >> expr)       <|> (reserved "def" >> try patternFunctionExpr <|> defineExpr)+      <|> declareRuleExpr+      <|> declareIdealExpr+      <|> declareDerivativeExpr+      <|> declareApplyExpr+      <|> declareMathFuncExpr+      <|> declareCasExpr       <|> declareSymbolExpr       <|> try patternInductiveExpr       <|> inductiveExpr@@ -218,11 +225,12 @@ inductiveTypeAtom :: Parser TypeExpr inductiveTypeAtom =       TEInt     <$ reserved "Integer"-  <|> TEMathExpr <$ reserved "MathExpr"+  <|> TEMathValue <$ reserved "MathValue"   <|> TEFloat   <$ reserved "Float"   <|> TEBool    <$ reserved "Bool"   <|> TEChar    <$ reserved "Char"   <|> TEString  <$ reserved "String"+  <|> TEFactor  <$ reserved "Factor"   <|> TEList    <$> brackets typeExpr   <|> TEVar     <$> typeNameIdent     -- Uppercase type names (Nat, Tree, etc.)   <|> TEVar     <$> inductiveTypeVar  -- Short lowercase type variables@@ -272,36 +280,56 @@   pos <- L.indentLevel   reserved "class"   -- Parse optional superclass constraints: extends Eq a-  (superclasses, classNm, typeParams) <- classHeader-  reserved "where"-  -- Parse methods - use alignSome for consistent indentation handling-  methods <- many $ try $ do-    _ <- indentGuardGT pos-    -- Check that this looks like a method definition-    notFollowedBy (reserved "def" <|> reserved "class" <|> reserved "instance" <|> reserved "inductive")-    classMethod+  (superclasses, classNm, typeParams) <- classHeader pos+  -- 'where' is optional: marker classes (no methods) can omit it+  hasWhere <- option False (True <$ reserved "where")+  methods <- if hasWhere+    then many $ try $ do+      _ <- indentGuardGT pos+      -- Check that this looks like a method definition+      notFollowedBy (reserved "def" <|> reserved "class" <|> reserved "instance" <|> reserved "inductive")+      classMethod+    else return []   return $ ClassDeclExpr $ ClassDecl classNm typeParams superclasses methods --- | Parse class header: "Ord a extends Eq a" or "Eq a"--- Note: type parameters are parsed until "where" or "extends" is encountered-classHeader :: Parser ([ConstraintExpr], String, [String])-classHeader = try withExtends <|> withoutExtends+-- | Parse class header: "Ord a extends Eq a" or "Ring a extends AddGroup a, MulMonoid a" or "Eq a"+-- Supports multiple superclass constraints separated by commas.+-- The basePos parameter is the indentation level of the 'class' keyword,+-- used to prevent consuming tokens from the next top-level declaration.+classHeader :: Pos -> Parser ([ConstraintExpr], String, [String])+classHeader basePos = try withExtends <|> withoutExtends   where     withExtends = do       classNm <- upperId       typeParams <- someTill typeVarIdent (lookAhead (reserved "extends"))       reserved "extends"-      -- Parse superclass constraints (single constraint only for now)-      superClassName <- upperId-      superTypeArgs <- manyTill typeVarIdent (lookAhead (reserved "where"))-      let constraints = [ConstraintExpr superClassName (map TEVar superTypeArgs)]+      -- Parse comma-separated superclass constraints: AddGroup a, MulMonoid a+      constraints <- superConstraint `sepBy1` symbol ","       return (constraints, classNm, typeParams) +    -- Parse a single superclass constraint: ClassName typeArg1 typeArg2 ...+    -- Type args are consumed until we see "where", ",", or a new top-level declaration+    superConstraint = do+      superClassName <- upperId+      superTypeArgs <- many (try $ do+        notFollowedBy (reserved "where" <|> void (symbol ","))+        guardIndented+        typeVarIdent)+      return $ ConstraintExpr superClassName (map TEVar superTypeArgs)+     withoutExtends = do       classNm <- upperId-      typeParams <- manyTill typeVarIdent (lookAhead (reserved "where"))+      typeParams <- many (try $ do+        notFollowedBy (reserved "where")+        guardIndented+        typeVarIdent)       return ([], classNm, typeParams) +    -- Reject tokens at the same indentation as 'class' (i.e., new top-level declarations)+    guardIndented = do+      curPos <- L.indentLevel+      guard (curPos > basePos)+ -- | Parse a single class method -- e.g., (==) (x: a) (y: a) : Bool --       (/=) (x: a) (y: a) : Bool := not (x == y)@@ -338,29 +366,41 @@   pos <- L.indentLevel   reserved "instance"   -- Parse optional instance constraints: Eq a =>-  (constraints, classNm, instTypes) <- instanceHeader-  reserved "where"+  (constraints, classNm, instTypes) <- instanceHeader pos+  hasWhere <- option False (True <$ reserved "where")   -- Parse method implementations (indented)-  methods <- instanceMethodsParser pos+  methods <- if hasWhere+    then instanceMethodsParser pos+    else return []   return $ InstanceDeclExpr $ InstanceDecl constraints classNm instTypes methods  -- | Parse instance header: "Eq Integer" or "{Eq a} Eq [a]"--- Note: instance types are parsed until "where" is encountered-instanceHeader :: Parser ([ConstraintExpr], String, [TypeExpr])-instanceHeader = try withConstraints <|> withoutConstraints+-- Note: instance types are parsed until "where" or end of declaration+instanceHeader :: Pos -> Parser ([ConstraintExpr], String, [TypeExpr])+instanceHeader basePos = try withConstraints <|> withoutConstraints   where     -- New syntax: {Eq a} Eq [a]     withConstraints = do       constraints <- typeConstraints       classNm <- upperId-      instTypes <- someTill typeAtomSimple (lookAhead (reserved "where"))+      instTypes <- many (try $ do+        notFollowedBy (reserved "where")+        guardIndented+        typeAtomSimple)       return (constraints, classNm, instTypes)      withoutConstraints = do       classNm <- upperId-      instTypes <- someTill typeAtomSimple (lookAhead (reserved "where"))+      instTypes <- many (try $ do+        notFollowedBy (reserved "where")+        guardIndented+        typeAtomSimple)       return ([], classNm, instTypes) +    guardIndented = do+      curPos <- L.indentLevel+      guard (curPos > basePos)+ -- | Parse instance methods instanceMethodsParser :: Pos -> Parser [InstanceMethod] instanceMethodsParser basePos = option [] $ do@@ -447,6 +487,73 @@   body <- pattern   return $ PatternFunctionDecl name typeParams params retType body +-- | Parse the `declare cas-*` family of the extensible CAS tower:+--+--   declare cas-type Q := <type>                     (transparent alias, D3)+--   declare cas-subtype A ⊂ B      (or A <: B)       (order edge, D1/D5)+--   declare cas-quotient Q := <base> by <reduce>     (quotient, M4)+--+-- The keyword lexes as `cas`, `-`, `<kind>` because identifiers cannot+-- contain hyphens; the three kinds share this prefix and dispatch on the+-- kind word.+declareCasExpr :: Parser TopExpr+declareCasExpr = try $ do+  pos <- L.indentLevel+  reserved "declare"+  keyword <- lowerId+  if keyword /= "cas"+    then fail "Expected 'cas-type', 'cas-subtype', or 'cas-quotient' after 'declare'"+    else return ()+  _ <- symbol "-"+  kind <- lowerId+  case kind of+    "type" -> do+      name <- upperId+      _ <- symbol ":="+      ty <- typeExprIndented pos+      return $ DeclareCasType name ty+    "subtype" -> do+      lhs <- typeExprIndented pos+      _ <- try (symbol "⊂") <|> symbol "<:"+      rhs <- typeExprIndented pos+      return $ DeclareCasSubtype lhs rhs+    "quotient" -> do+      name <- upperId+      _ <- symbol ":="+      base <- typeExprIndentedStop keywordBy pos+      sep <- lowerId+      if sep /= "by"+        then fail "Expected 'by' after the base type of declare cas-quotient"+        else return ()+      reduceE <- expr+      return $ DeclareCasQuotient name base reduceE+    other -> fail ("Expected type/subtype/quotient after 'cas-', got: " ++ other)+  where+    keywordBy = try (do w <- lowerId+                        if w == "by" then return () else fail "not 'by'")++-- | Like 'typeExprWithApp', but every token must be indented deeper than the+-- given column, and parsing stops in front of `stop`. Used where a type ends+-- a declaration (the alias body of `declare cas-type X := <type>`) or is+-- followed by a keyword (`by` in `declare cas-quotient`), so the greedy+-- type-atom sequence does not swallow what comes next.+typeExprIndentedStop :: Parser () -> Pos -> Parser TypeExpr+typeExprIndentedStop stop base = do+  atoms <- some (try (indentGuardGT base >> notFollowedBy stop >> typeAtomSimple))+  rest <- optional (try (indentGuardGT base >> notFollowedBy stop >> symbol "->")+                    >> typeExprIndentedStop stop base)+  let baseType = case atoms of+                   [t]    -> t+                   (t:ts) -> TEApp t ts+                   []     -> error "unreachable"+  return $ case rest of+    Nothing -> baseType+    Just r  -> TEFun baseType r++-- | 'typeExprIndentedStop' with no stop keyword.+typeExprIndented :: Pos -> Parser TypeExpr+typeExprIndented = typeExprIndentedStop (fail "no stop keyword")+ declareSymbolExpr :: Parser TopExpr declareSymbolExpr = try $ do   reserved "declare"@@ -464,6 +571,139 @@     typeAtomSimple   return $ DeclareSymbol names mType +-- | Parse a `declare rule` declaration.+--+--   declare rule auto term i^2 = -1+--   declare rule trig_pythagorean poly (sin $x)^2 + (cos #x)^2 = 1+--   declare rule rationalize_sqrt frac $x / (sqrt $y) = x * sqrt y / y+--+-- Form: `declare rule [auto|<name>] [term|poly|frac] <lhs> = <rhs>`.+--+-- Parsing strategy: `=` is a regular binary operator in expr, so the whole+-- `<lhs> = <rhs>` is captured as a single InfixExpr node, which we then split+-- into LHS and RHS.+declareRuleExpr :: Parser TopExpr+declareRuleExpr = try $ do+  reserved "declare"+  keyword <- lowerId+  if keyword /= "rule"+    then fail "Expected 'rule' after 'declare'"+    else return ()+  -- Parse `auto` or a rule name (lowercase identifier)+  qualifier <- lowerId+  let ruleName = if qualifier == "auto" then Nothing else Just qualifier+  -- Parse rule level: term / poly / frac+  levelName <- lowerId+  level <- case levelName of+    "term" -> return TermRuleLevel+    "poly" -> return PolyRuleLevel+    "frac" -> return FracRuleLevel+    other  -> fail ("Expected term/poly/frac after rule level, got: " ++ other)+  -- Parse LHS as a Pattern with auto-quoting (so `i^2` works as ValuePat-only,+  -- and `(sin $x)^2` parses with the `$x` correctly bound).+  lhs <- ruleLhsPattern+  -- The separator `=` between LHS pattern and RHS expression+  _ <- try (operator "=")+  -- Parse RHS as a normal Expr; use `exprWithoutWhere` to avoid eating a+  -- trailing `where` that belongs to subsequent declarations.+  rhs <- exprWithoutWhere+  return $ DeclareRule ruleName level lhs rhs++-- | Parse a `declare ideal` declaration (G3 of design/cas-simplification.md).+--+--   declare ideal [w^2 + w + 1]+--   declare ideal [(sin θ)^2 + (cos θ)^2 - 1]+--+-- The generators are ordinary expressions; at desugar time they receive+-- the same rule-free treatment as `declare rule` right-hand sides, so a+-- generator that the active auto rules would collapse (e.g. a Pythagorean+-- relation) is safe to write plainly.+declareIdealExpr :: Parser TopExpr+declareIdealExpr = try $ do+  reserved "declare"+  keyword <- lowerId+  if keyword /= "ideal"+    then fail "Expected 'ideal' after 'declare'"+    else return ()+  _ <- symbol "["+  gens <- sepBy expr (symbol ",")+  _ <- symbol "]"+  return $ DeclareIdeal gens++-- | Split a parsed expression at the top-level `=` operator, returning the LHS+-- and RHS sub-expressions. Returns `Nothing` if the expression does not have+-- `=` at its top level. Used by `declare derivative` (whose LHS is a plain+-- identifier so we can keep parsing it as an Expr).+extractRuleSides :: Expr -> Maybe (Expr, Expr)+extractRuleSides (InfixExpr op lhs rhs) | repr op == "=" = Just (lhs, rhs)+extractRuleSides _ = Nothing++-- | Parse a `declare derivative` declaration (Phase 6.3 of type-cas).+--+--   declare derivative sin = cos+--   declare derivative log = \x -> 1 / x+--+-- Same body-splitting trick as `declare rule`: the body is parsed as a single+-- expression with `=` as a top-level binary operator, then the LHS is required+-- to be a single identifier (the function name) and the RHS is kept as the+-- derivative expression.+declareDerivativeExpr :: Parser TopExpr+declareDerivativeExpr = try $ do+  reserved "declare"+  keyword <- lowerId+  if keyword /= "derivative"+    then fail "Expected 'derivative' after 'declare'"+    else return ()+  body <- exprWithoutWhere+  case extractRuleSides body of+    Just (VarExpr name, rhs) -> return $ DeclareDerivative name rhs+    Just (_, _) ->+      fail "expected `declare derivative <name> = <expr>` (LHS must be a plain identifier)"+    Nothing -> fail "expected `declare derivative <name> = <expr>`"++-- | Parse a `declare mathfunc` declaration (Phase 6.3 part 5).+--+--   declare mathfunc sin+--   declare mathfunc sqrt : MathValue -> MathValue+--+-- Generates a wrapper function that quotes the symbol on application,+-- so user-level expressions can call it like a regular function.+declareMathFuncExpr :: Parser TopExpr+declareMathFuncExpr = try $ do+  reserved "declare"+  keyword <- lowerId+  if keyword /= "mathfunc"+    then fail "Expected 'mathfunc' after 'declare'"+    else return ()+  name <- lowerId+  -- Optional type annotation, parsed but currently unused+  mType <- optional $ try $ do+    _ <- symbol ":"+    typeAtomSimple+  return $ DeclareMathFunc name mType++-- | Parse a `declare apply` declaration (Phase A of declare apply impl).+--+--   declare apply sin x := if x = 0 then 0 else 'sin x+--   declare apply sqrt x := ...+--+-- The function name must already be known via `declare mathfunc`. The body+-- has the same evaluation semantics as a plain `def`, except that it+-- overrides the wrapper generated by `declare mathfunc` so user-level calls+-- to <name> dispatch through this body.+declareApplyExpr :: Parser TopExpr+declareApplyExpr = try $ do+  reserved "declare"+  keyword <- lowerId+  if keyword /= "apply"+    then fail "Expected 'apply' after 'declare'"+    else return ()+  name <- lowerId+  args <- many lowerId+  _    <- symbol ":="+  body <- expr+  return $ DeclareApply name args body+ defineExpr :: Parser TopExpr defineExpr = try defineWithType <|> defineWithoutType   where@@ -612,7 +852,7 @@ typeAtomSimple :: Parser TypeExpr typeAtomSimple =       TEInt     <$ reserved "Integer"-  <|> TEMathExpr <$ reserved "MathExpr"+  <|> TEMathValue <$ reserved "MathValue"   <|> TEFloat   <$ reserved "Float"   <|> TEBool    <$ reserved "Bool"   <|> TEChar    <$ reserved "Char"@@ -623,6 +863,12 @@   <|> try vectorTypeExpr   <|> try matrixTypeExpr   <|> try diffFormTypeExpr+  -- New CAS types+  <|> try factorTypeExpr+  <|> try termTypeExpr+  <|> try fracTypeExpr+  <|> try polyTypeExpr+  <|> try matcherSlotTypeExpr   <|> TEMatcher <$> (reserved "Matcher" >> typeAtomOrParenType)   <|> TEPattern <$> (reserved "Pattern" >> typeAtomOrParenType)   <|> TEVar     <$> typeVarIdent      -- lowercase type variables (a, b, etc.)@@ -633,7 +879,7 @@ typeAtom :: Parser TypeExpr typeAtom =       TEInt     <$ reserved "Integer"-  <|> TEMathExpr <$ reserved "MathExpr"+  <|> TEMathValue <$ reserved "MathValue"   <|> TEFloat   <$ reserved "Float"   <|> TEBool    <$ reserved "Bool"   <|> TEChar    <$ reserved "Char"@@ -644,6 +890,12 @@   <|> try vectorTypeExpr   <|> try matrixTypeExpr   <|> try diffFormTypeExpr+  -- New CAS types+  <|> try factorTypeExpr+  <|> try termTypeExpr+  <|> try fracTypeExpr+  <|> try polyTypeExpr+  <|> try matcherSlotTypeExpr   <|> TEMatcher <$> (reserved "Matcher" >> typeAtomOrParenType)   <|> TEPattern <$> (reserved "Pattern" >> typeAtomOrParenType)   <|> TEVar     <$> typeVarIdent      -- lowercase type variables (a, b, etc.)@@ -661,7 +913,7 @@     then fail $ "Reserved type keyword: " ++ name     else return name   where-    typeReservedKeywords = ["Integer", "MathExpr", "Float", "Bool", "Char", "String", "Matcher", "Pattern", "Tensor", "Vector", "Matrix", "IO"]+    typeReservedKeywords = ["Integer", "MathValue", "Float", "Bool", "Char", "String", "Matcher", "MatcherSlot", "Pattern", "Tensor", "Vector", "Matrix", "IO", "Factor", "Frac", "Poly"]  tensorTypeExpr :: Parser TypeExpr tensorTypeExpr = do@@ -688,7 +940,74 @@   elemType <- typeAtomOrParenType   return $ TEDiffForm elemType +-- | Parse Factor type+factorTypeExpr :: Parser TypeExpr+factorTypeExpr = TEFactor <$ reserved "Factor" +-- | Parse Term type (e.g., Term Integer [x] or Term Integer [..])+-- A Term is a single monomial: coefficient × monomial over the given atom set.+termTypeExpr :: Parser TypeExpr+termTypeExpr = do+  _ <- reserved "Term"+  innerType <- typeAtomOrParenType+  symbolSet <- symbolSetExpr+  return $ TETerm innerType symbolSet++-- | Parse Frac type (e.g., Frac Integer)+fracTypeExpr :: Parser TypeExpr+fracTypeExpr = do+  _ <- reserved "Frac"+  innerType <- typeAtomOrParenType+  return $ TEFrac innerType++-- | Parse Poly type (e.g., Poly Integer [x, y] or Poly Integer [..])+polyTypeExpr :: Parser TypeExpr+polyTypeExpr = do+  _ <- reserved "Poly"+  coeffType <- typeAtomOrParenType+  symbolSet <- symbolSetExpr+  return $ TEPoly coeffType symbolSet++-- | Parse a MatcherSlot type. "MatcherSlot a b" reads two atoms (structural / target);+-- "MatcherSlot a" is sugar for "MatcherSlot a a".+matcherSlotTypeExpr :: Parser TypeExpr+matcherSlotTypeExpr = do+  _ <- reserved "MatcherSlot"+  s <- typeAtomOrParenType+  mt <- optional typeAtomOrParenType+  return $ case mt of+    Just t  -> TEMatcherSlot s t+    Nothing -> TEMatcherSlot s s++-- | Parse a symbol set expression+-- Either [..] for open, or [x, y, sqrt 2, sin x, ...] for closed.+-- Closed slots accept simple identifiers as well as function-applied forms+-- like `sqrt 2`, `sin x`. Each atom is parsed into a structured `TypeAtomExpr`.+symbolSetExpr :: Parser SymbolSetExpr+symbolSetExpr = brackets $ openSymbolSet <|> closedSymbolSet+  where+    -- [..] - open symbol set+    openSymbolSet = SSEOpen <$ symbol ".."+    -- [x, y, sqrt 2, ...] - closed symbol set with atom expressions.+    closedSymbolSet = SSEClosed <$> atomExpr `sepBy` symbol ","+    -- An atom is either a simple lowercase identifier (`x`) or a function+    -- applied to simple-name / integer arguments (`sqrt 2`, `sin x`).+    -- Nested atoms with parens or arithmetic are not yet supported.+    atomExpr :: Parser TypeAtomExpr+    atomExpr = lexeme $ do+      hd <- atomIdent+      args <- many (try (some (char ' ') >> atomArg))+      return $ if null args then TAEName hd else TAEApp hd args+    atomIdent :: Parser String+    atomIdent = do+      c <- lowerChar+      cs <- many identChar+      return (c : cs)+    atomArg :: Parser TypeAtomExpr+    atomArg = (TAEName <$> atomIdent) <|> (TAEInt <$> atomInteger)+    atomInteger :: Parser Integer+    atomInteger = read <$> some digitChar+ typeVarIdent :: Parser String typeVarIdent = lexeme $ do   c <- lowerChar@@ -698,7 +1017,7 @@     then fail $ "Reserved word: " ++ name     else return name   where-    typeReservedWords = ["Integer", "MathExpr", "Float", "Bool", "Char", "String", "Matcher", "Pattern", "Tensor", "Vector", "Matrix", "DiffForm"]+    typeReservedWords = ["Integer", "MathValue", "Float", "Bool", "Char", "String", "Matcher", "MatcherSlot", "Pattern", "Tensor", "Vector", "Matrix", "DiffForm", "Factor", "Term", "Frac", "Poly"]  expr :: Parser Expr expr = do@@ -726,10 +1045,24 @@    <|> algebraicDataMatcherExpr    <|> tensorExpr    <|> functionExpr+   <|> simplifyUsingExpr    <|> refsExpr    <|> atomOrApplyExpr    <?> "expression" +-- | Parse `simplify <expr> using <rule_name>` (Phase 7.6 skeleton).+-- Body is parsed as `exprWithoutWhere` so the trailing `using <name>` is+-- captured separately. The runtime semantics is a no-op for now (returns the+-- evaluated expression unchanged) — the rule application engine will+-- replace this stub.+simplifyUsingExpr :: Parser Expr+simplifyUsingExpr = try $ do+  reserved "simplify"+  body <- exprWithoutWhere+  reserved "using"+  ruleName <- lowerId+  return $ SimplifyUsingExpr body ruleName+ -- Also parses exprInOp opExpr :: Parser Expr opExpr = do@@ -964,12 +1297,15 @@  refsExpr :: Parser Expr refsExpr =-      (reserved "subrefs"   >> SubrefsExpr  False <$> atomExpr <*> atomExpr)-  <|> (reserved "subrefs!"  >> SubrefsExpr  True  <$> atomExpr <*> atomExpr)-  <|> (reserved "suprefs"   >> SuprefsExpr  False <$> atomExpr <*> atomExpr)+      -- The "!" variants must come first: `reserved "subrefs"` succeeds on+      -- the input "subrefs!" (the "!" is not an identifier character), which+      -- would make the "!" variants unreachable if they were tried second.+      (reserved "subrefs!"  >> SubrefsExpr  True  <$> atomExpr <*> atomExpr)+  <|> (reserved "subrefs"   >> SubrefsExpr  False <$> atomExpr <*> atomExpr)   <|> (reserved "suprefs!"  >> SuprefsExpr  True  <$> atomExpr <*> atomExpr)-  <|> (reserved "userRefs"  >> UserrefsExpr False <$> atomExpr <*> atomExpr)+  <|> (reserved "suprefs"   >> SuprefsExpr  False <$> atomExpr <*> atomExpr)   <|> (reserved "userRefs!" >> UserrefsExpr True  <$> atomExpr <*> atomExpr)+  <|> (reserved "userRefs"  >> UserrefsExpr False <$> atomExpr <*> atomExpr)  collectionExpr :: Parser Expr collectionExpr = symbol "[" >> betweenOrFromExpr <|> elementsExpr@@ -984,16 +1320,30 @@     elementsExpr = CollectionExpr <$> (sepBy expr comma <* symbol "]")  -- Parse an atomic expression starting with '(', which can be:+--   * a type-annotated expression `(e : T)` (Phase D) --   * a tuple --   * an arbitrary expression wrapped with parenthesis --   * section tupleOrParenExpr :: Parser Expr tupleOrParenExpr = do-  elems <- symbol "(" >> try (sepBy expr comma <* symbol ")") <|> (section <* symbol ")")-  case elems of-    [x] -> return x                 -- expression wrapped in parenthesis-    _   -> return $ TupleExpr elems -- tuple+  _ <- symbol "("+  try typeAnnotated <|> tupleOrSection   where+    -- `(e : T)` — try this first. If `:` is missing, fall through.+    typeAnnotated :: Parser Expr+    typeAnnotated = do+      e  <- expr+      _  <- symbol ":"+      ty <- typeExpr+      _  <- symbol ")"+      return (TypeAnnotation e ty)++    tupleOrSection :: Parser Expr+    tupleOrSection = do+      elems <- try (sepBy expr comma <* symbol ")") <|> (section <* symbol ")")+      case elems of+        [x] -> return x                 -- expression wrapped in parenthesis+        _   -> return $ TupleExpr elems -- tuple     section :: Parser [Expr]     -- Start from right, in order to parse expressions like (-1 +) correctly     section = (:[]) <$> (rightSection <|> leftSection)@@ -1120,13 +1470,13 @@     -- Parse negative number literals (-1, -2.5, etc.)     -- Only recognize as negative literal if there's no space after '-'     negativeFloatLiteral = lexeme $ do-      char '-'+      _ <- char '-'       notFollowedBy spaceChar       n <- L.float       return $ FloatExpr (negate n)-    +     negativeIntegerLiteral = lexeme $ do-      char '-'+      _ <- char '-'       notFollowedBy spaceChar       n <- L.decimal       return $ IntegerExpr (negate n)@@ -1232,6 +1582,84 @@            <|> LaterPatVar <$ symbol "@"            <?> "atomic pattern" +-- | Parse a `declare rule` LHS as a Pattern, with auto-quoting:+--   - bare lowercase identifiers and integer literals become `ValuePat`-wrapped+--     Exprs (so existing rules like `i^2 = -1` continue to work),+--   - `$x`           → PatVar "x" (binds variable x),+--   - `#expr`        → ValuePat expr (references existing bindings),+--   - `_`            → WildCard,+--   - infix `+ * / ^` work as InfixPat via patternOps,+--   - juxtaposition `f a b` becomes either InductivePat (when `f` is a known+--     mathExpr matcher constructor like `apply1`, `term`, `frac`, ...) or+--     PApplyPat (when `f` is a value reference, e.g. `sin`).+ruleLhsPattern :: Parser Pattern+ruleLhsPattern = do+  ops <- gets patternOps+  makeExprParser ruleLhsApply (makePatternTable ops)+  <?> "rule LHS pattern"++ruleLhsApply :: Parser Pattern+ruleLhsApply = do+  first <- ruleLhsAtom+  args  <- many (try ruleLhsAtom)+  return $ case args of+    [] -> first+    _  -> case first of+      InductivePat n []    -> InductivePat n args+      ValuePat funcExpr    -> PApplyPat funcExpr args+      _                     -> first++ruleLhsAtom :: Parser Pattern+ruleLhsAtom =+       (WildCard <$ try (symbol "_" <* notFollowedBy alphaNumChar))+   <|> (PatVar <$> patVarLiteral)                         -- $x+   <|> (ValuePat <$> (char '#' >> atomExpr))              -- #expr+   <|> (ValuePat . QuoteSymbolExpr+          <$> try (char '\'' >> atomExpr'))               -- 'name (auto-quoted)+   <|> ruleLhsConstantOrIdent                              -- 42, x, sin (auto-quote)+   <|> ruleLhsEmptyList                                    -- []+   <|> try ruleLhsTupleOrParen                             -- (a, b, ...) or (...)+   <|> parens ruleLhsPattern                               -- (...) fallback+   <?> "rule LHS atom"++-- | Parse `[]` as an empty-collection / empty-list pattern. Useful inside+-- rule LHS when matching a single-element assocMultiset like+-- `((apply1 #f $a, $n) :: [])`.+ruleLhsEmptyList :: Parser Pattern+ruleLhsEmptyList = try $ symbol "[]" >> return (InductivePat "[]" [])++-- | Parse a parenthesised pattern that may be a tuple `(a, b, ...)`.+-- A single-element form is just the pattern unwrapped from parens.+ruleLhsTupleOrParen :: Parser Pattern+ruleLhsTupleOrParen = parens $ do+  ps <- ruleLhsPattern `sepBy1` symbol ","+  return $ case ps of+    [single] -> single+    _        -> TuplePat ps++ruleLhsConstantOrIdent :: Parser Pattern+ruleLhsConstantOrIdent =+       (ValuePat . ConstantExpr <$> try numericExpr)      -- 42, -1, 3.14+   <|> ruleLhsLowerIdent++ruleLhsLowerIdent :: Parser Pattern+ruleLhsLowerIdent = do+  name <- lowerId+  return $ if name `elem` mathExprMatcherConstructors+              then InductivePat name []+              else ValuePat (VarExpr name)++-- | Hardcoded list of mathExpr/mathValue matcher constructor names. Identifiers+-- in this list are treated as pattern constructors (InductivePat); all other+-- bare lowercase identifiers in a rule LHS are auto-quoted as ValuePat.+mathExprMatcherConstructors :: [String]+mathExprMatcherConstructors =+  [ "frac", "poly", "plus", "term", "mult", "symbol"+  , "apply1", "apply2", "apply3", "apply4"+  , "quote", "func"+  , "sub", "sup", "user"+  ]+ ppPattern :: Parser PrimitivePatPattern ppPattern = PPInductivePat <$> lowerId <*> many ppAtom         <|> do ops <- gets patternOps@@ -1267,20 +1695,20 @@       ]      pdApplyOrAtom :: Parser PrimitiveDataPattern-    pdApplyOrAtom = try mathExprPrimitivePattern+    pdApplyOrAtom = try mathValuePrimitivePattern                 <|> PDInductivePat <$> upperId <*> many pdAtom                 <|> pdAtom     -    -- MathExpr primitive patterns-    mathExprPrimitivePattern :: Parser PrimitiveDataPattern-    mathExprPrimitivePattern = do+    -- MathValue primitive patterns+    mathValuePrimitivePattern :: Parser PrimitiveDataPattern+    mathValuePrimitivePattern = do       name <- upperId       case name of-        "Div" -> do+        "Frac" -> do           args <- many pdAtom           case args of-            [p1, p2] -> return $ PDDivPat p1 p2-            _ -> fail "Div requires exactly 2 arguments"+            [p1, p2] -> return $ PDFracPat p1 p2+            _ -> fail "Frac requires exactly 2 arguments"         "Plus" -> do           args <- many pdAtom           case args of@@ -1341,7 +1769,7 @@           case args of             [p] -> return $ PDUserPat p             _ -> fail "User requires exactly 1 argument"-        _ -> fail "Not a MathExpr primitive pattern"+        _ -> fail "Not a MathValue primitive pattern"  pdAtom :: Parser PrimitiveDataPattern pdAtom = PDWildCard    <$ symbol "_"@@ -1593,6 +2021,8 @@   , "infixl"   , "infixr"   , "infix"+  , "simplify"+  , "using"   ]  --
hs-src/Language/Egison/Pretty.hs view
@@ -57,12 +57,45 @@           parens (pretty pname <+> pretty ":" <+> pretty ptype)) params     in pretty "def" <+> pretty "pattern" <+> pretty name <+> typeParamsDoc <+>         paramsDoc <+> pretty ":" <+> pretty retType <+> pretty ":=" <+> pretty body+  pretty (DeclareCasType name typeExpr) =+    pretty "declare" <+> pretty "cas-type" <+> pretty name <+>+    pretty ":=" <+> pretty typeExpr+  pretty (DeclareCasSubtype lhs rhs) =+    pretty "declare" <+> pretty "cas-subtype" <+> pretty lhs <+>+    pretty "<:" <+> pretty rhs+  pretty (DeclareCasQuotient name base reduceE) =+    pretty "declare" <+> pretty "cas-quotient" <+> pretty name <+>+    pretty ":=" <+> pretty base <+> pretty "by" <+> pretty reduceE   pretty (DeclareSymbol names mTypeExpr) =     let namesDoc = hsep $ punctuate (pretty ",") (map pretty names)         typeDoc = case mTypeExpr of                     Just typeExpr -> pretty ":" <+> pretty typeExpr                     Nothing -> emptyDoc     in pretty "declare" <+> pretty "symbol" <+> namesDoc <+> typeDoc+  pretty (DeclareRule mname level lhs rhs) =+    let nameDoc  = case mname of+                     Nothing -> pretty "auto"+                     Just n  -> pretty n+        levelDoc = case level of+                     TermRuleLevel -> pretty "term"+                     PolyRuleLevel -> pretty "poly"+                     FracRuleLevel -> pretty "frac"+    in pretty "declare" <+> pretty "rule" <+> nameDoc <+> levelDoc <+>+       pretty lhs <+> pretty "=" <+> pretty rhs+  pretty (DeclareIdeal gens) =+    pretty "declare" <+> pretty "ideal" <+>+    pretty "[" <> hsep (punctuate comma (map pretty gens)) <> pretty "]"+  pretty (DeclareDerivative name rhs) =+    pretty "declare" <+> pretty "derivative" <+> pretty name <+>+    pretty "=" <+> pretty rhs+  pretty (DeclareMathFunc name mType) =+    let typeDoc = case mType of+                    Nothing -> emptyDoc+                    Just t  -> pretty ":" <+> pretty t+    in pretty "declare" <+> pretty "mathfunc" <+> pretty name <+> typeDoc+  pretty (DeclareApply name args body) =+    pretty "declare" <+> pretty "apply" <+> pretty name <+>+    hsep (map pretty args) <+> pretty ":=" <+> pretty body   pretty _ = error "Unsupported topexpr"  instance Pretty ConstantExpr where@@ -241,7 +274,7 @@  instance Pretty TypeExpr where   pretty TEInt = pretty "Integer"-  pretty TEMathExpr = pretty "MathExpr"+  pretty TEMathValue = pretty "MathValue"   pretty TEFloat = pretty "Float"   pretty TEBool = pretty "Bool"   pretty TEChar = pretty "Char"@@ -252,6 +285,7 @@   pretty (TETuple ts) = parens (hsep (punctuate comma (map pretty ts)))   pretty (TEFun t1 t2) = pretty t1 <+> pretty "->" <+> pretty t2   pretty (TEMatcher t) = pretty "Matcher" <+> pretty t+  pretty (TEMatcherSlot s t) = pretty "MatcherSlot" <+> pretty s <+> pretty t   pretty (TEPattern t) = pretty "Pattern" <+> pretty t   pretty (TEIO t) = pretty "IO" <+> pretty t   pretty (TETensor t) = pretty "Tensor" <+> pretty t@@ -474,6 +508,11 @@      pretty (IFunctionExpr xs) = pretty "function" <+> tupled (map pretty xs) +  -- Type-annotated expression `(e : T)` (reshape node). Shown with the+  -- internal type's show form; used mainly in type-error contexts.+  pretty (IReshape ty e) =+    parens (pretty' e <+> pretty ":" <+> pretty (show ty))+ prettyRefExpr :: IExpr -> Doc ann prettyRefExpr e = if isAtom e then pretty e else parens (pretty e) @@ -521,8 +560,8 @@   pretty (PDConsPat pat1 pat2) = pretty pat1 <+> pretty "::" <+> pretty pat2   pretty (PDSnocPat pat1 pat2) = pretty pat1 <+> pretty "*:" <+> pretty pat2   pretty (PDConstantPat c) = pretty c-  -- MathExpr primitive patterns-  pretty (PDDivPat p1 p2) = applyLike [pretty "Div", pretty p1, pretty p2]+  -- MathValue primitive patterns+  pretty (PDFracPat p1 p2) = applyLike [pretty "Frac", pretty p1, pretty p2]   pretty (PDPlusPat p) = applyLike [pretty "Plus", pretty p]   pretty (PDTermPat p1 p2) = applyLike [pretty "Term", pretty p1, pretty p2]   pretty (PDSymbolPat p1 p2) = applyLike [pretty "Symbol", pretty p1, pretty p2]@@ -826,8 +865,16 @@      TIMatcherExpr patDefs ->     pretty "matcher" <+> vsep (map prettyPatDef patDefs)-    where prettyPatDef (pat, expr, _bindings) = pretty pat <+> pretty "->" <+> prettyTIExprWithType expr+    where prettyPatDef (pat, expr, bindings) =+            pretty pat <+> pretty "->" <+> prettyTIExprWithType expr+              <+> pretty "with" <+> vsep (map prettyArm bindings)+          prettyArm (dp, e) = pretty "|" <+> pretty (show dp) <+> pretty "->" <+> prettyTIExprWithType e +  TIRuntimeDispatch className methodName _candidates args ->+    pretty "<runtime-dispatch" <+> pretty className <> pretty "."+        <> pretty methodName <> pretty ">"+      <+> hsep (map prettyTIExprWithType args)+ instance Pretty TITopExpr where   pretty (TIDefine scheme var tiexpr) =     let typeStr = prettyTypeScheme scheme@@ -884,8 +931,8 @@  -- Helper function to pretty print a single constraint as Doc prettyConstraintDoc :: Types.Constraint -> Doc ann-prettyConstraintDoc (Types.Constraint className tyArg) = -  pretty className <+> prettyTypeDoc tyArg+prettyConstraintDoc (Types.Constraint className tyArgs) =+  pretty className <> hsep (map (\t -> space <> prettyTypeDoc t) tyArgs)  -- Helper function to pretty print Type as Doc prettyTypeDoc :: Types.Type -> Doc ann@@ -909,6 +956,7 @@     prettyHashValueTypeDoc t@(Types.TFun _ _) = parens (prettyTypeDoc t)     prettyHashValueTypeDoc t = prettyTypeDoc t prettyTypeDoc (Types.TMatcher t) = pretty "Matcher" <+> prettyTypeDoc t+prettyTypeDoc (Types.TMatcherSlot s t) = pretty "MatcherSlot" <+> prettyTypeDoc s <+> prettyTypeDoc t prettyTypeDoc (Types.TIO t) = pretty "IO" <+> prettyTypeDoc t prettyTypeDoc (Types.TIORef t) = pretty "IORef" <+> prettyTypeDoc t prettyTypeDoc (Types.TTensor t) = pretty "Tensor" <+> prettyTypeDoc t@@ -916,11 +964,22 @@ prettyTypeDoc (Types.TInductive name ts) = hsep (pretty name : map prettyTypeDoc ts) prettyTypeDoc Types.TAny = pretty "_" prettyTypeDoc Types.TPort = pretty "Port"-prettyTypeDoc Types.TMathExpr = pretty "MathExpr"+prettyTypeDoc Types.TMathValue = pretty "MathValue" prettyTypeDoc Types.TPolyExpr = pretty "PolyExpr" prettyTypeDoc Types.TTermExpr = pretty "TermExpr" prettyTypeDoc Types.TSymbolExpr = pretty "SymbolExpr" prettyTypeDoc Types.TIndexExpr = pretty "IndexExpr"+prettyTypeDoc Types.TFactor = pretty "Factor"+prettyTypeDoc (Types.TFrac t) = pretty "Frac" <+> prettyTypeDoc t+prettyTypeDoc (Types.TTerm t ss) = pretty "Term" <+> prettyTypeDoc t <+> prettySymbolSetDoc ss+prettyTypeDoc (Types.TPoly t ss) = pretty "Poly" <+> prettyTypeDoc t <+> prettySymbolSetDoc ss++-- Helper to pretty print a symbol set (of Poly/Term types) as Doc+prettySymbolSetDoc :: Types.SymbolSet -> Doc ann+prettySymbolSetDoc (Types.SymbolSetClosed atoms) =+  brackets (hsep (punctuate comma (map (pretty . Types.prettyTypeAtomValue) atoms)))+prettySymbolSetDoc Types.SymbolSetOpen = pretty "[..]"+prettySymbolSetDoc (Types.SymbolSetVar (Types.TyVar v)) = pretty ("[" ++ v ++ "..]")  class Complex a where   isAtom :: a -> Bool
hs-src/Language/Egison/PrettyMath/AST.hs view
@@ -6,9 +6,9 @@ -}  module Language.Egison.PrettyMath.AST-  ( MathExpr(..)+  ( MathValue(..)   , MathIndex(..)-  , ToMathExpr(..)+  , ToMathValue(..)   , isSub   , parseExpr   ) where@@ -18,26 +18,26 @@  import qualified Language.Egison.Data          as E import qualified Language.Egison.IExpr         as E-import qualified Language.Egison.Math.Expr     as E+import qualified Language.Egison.Math.CAS      as CAS -data MathExpr+data MathValue   = Atom String [MathIndex]   | NegativeAtom String-  | Plus [MathExpr]-  | Multiply [MathExpr]-  | Div MathExpr MathExpr-  | Power MathExpr MathExpr-  | Func MathExpr [MathExpr]-  | Tensor [MathExpr] [MathIndex]-  | Tuple [MathExpr]-  | Collection [MathExpr]-  | Quote MathExpr-  | Partial MathExpr [MathExpr]+  | Plus [MathValue]+  | Multiply [MathValue]+  | Div MathValue MathValue+  | Power MathValue MathValue+  | Func MathValue [MathValue]+  | Tensor [MathValue] [MathIndex]+  | Tuple [MathValue]+  | Collection [MathValue]+  | Quote MathValue+  | Partial MathValue [MathValue]   deriving (Eq, Show)  data MathIndex-  = Super MathExpr-  | Sub MathExpr+  = Super MathValue+  | Sub MathValue   deriving (Eq, Show)  isSub :: MathIndex -> Bool@@ -45,93 +45,95 @@ isSub _       = False  -class ToMathExpr a where-  toMathExpr :: a -> MathExpr+class ToMathValue a where+  toMathValue :: a -> MathValue -instance ToMathExpr E.EgisonValue where-  toMathExpr (E.ScalarData s)  = toMathExpr s-  toMathExpr (E.Tuple es)      = Tuple (map toMathExpr es)-  toMathExpr (E.Collection es) = Collection (map toMathExpr (toList es))-  toMathExpr (E.TensorData t)  = toMathExpr t-  toMathExpr e                 = Atom (show e) []+instance ToMathValue E.EgisonValue where+  toMathValue (E.CASData cv)    = toMathValue cv+  toMathValue (E.Tuple es)      = Tuple (map toMathValue es)+  toMathValue (E.Collection es) = Collection (map toMathValue (toList es))+  toMathValue (E.TensorData t)  = toMathValue t+  toMathValue e                 = Atom (show e) [] -instance ToMathExpr a => ToMathExpr (E.Tensor a) where-  toMathExpr (E.Scalar _)       = undefined-  toMathExpr (E.Tensor [_] xs js) = Tensor (map toMathExpr (toList xs)) (map toMathIndex js)-  toMathExpr (E.Tensor [_, n] xs js) = Tensor (f (fromIntegral n) (map toMathExpr (toList xs))) (map toMathIndex js)+instance ToMathValue a => ToMathValue (E.Tensor a) where+  toMathValue (E.Scalar _)       = undefined+  toMathValue (E.Tensor [_] xs js) = Tensor (map toMathValue (toList xs)) (map toMathIndex js)+  toMathValue (E.Tensor [_, n] xs js) = Tensor (f (fromIntegral n) (map toMathValue (toList xs))) (map toMathIndex js)     where       f _ [] = []       f n xs = Tensor (take n xs) [] : f n (drop n xs)-  toMathExpr (E.Tensor _ _ _) = undefined--instance ToMathExpr E.ScalarData where-  toMathExpr (E.Div p (E.Plus [E.Term 1 []])) = toMathExpr p-  toMathExpr (E.Div p1 p2)                    = Div (toMathExpr p1) (toMathExpr p2)+  toMathValue (E.Tensor _ _ _) = undefined -instance ToMathExpr E.PolyExpr where-  toMathExpr (E.Plus [])  = Atom "0" []-  toMathExpr (E.Plus [x]) = toMathExpr x-  toMathExpr (E.Plus xs)  = Plus (map toMathExpr xs)+-- CASValue instances+instance ToMathValue CAS.CASValue where+  toMathValue (CAS.CASInteger n) = toMathValue n+  toMathValue (CAS.CASFactor sym) = toMathValue sym+  toMathValue (CAS.CASPoly []) = Atom "0" []+  toMathValue (CAS.CASPoly [t]) = toMathValue t+  toMathValue (CAS.CASPoly ts) = Plus (map toMathValue ts)+  toMathValue (CAS.CASFrac num (CAS.CASInteger 1)) = toMathValue num+  toMathValue (CAS.CASFrac num (CAS.CASPoly [CAS.CASTerm (CAS.CASInteger 1) []])) = toMathValue num+  toMathValue (CAS.CASFrac num denom) = Div (toMathValue num) (toMathValue denom) -instance ToMathExpr E.TermExpr where-  toMathExpr (E.Term n [])  = toMathExpr n-  toMathExpr (E.Term 1 [x]) = toMathExpr x-  toMathExpr (E.Term 1 xs)  = Multiply (map toMathExpr xs)-  toMathExpr (E.Term n xs)  = Multiply (toMathExpr n : map toMathExpr xs)+instance ToMathValue CAS.CASTerm where+  toMathValue (CAS.CASTerm coeff []) = toMathValue coeff+  toMathValue (CAS.CASTerm (CAS.CASInteger 1) [x]) = toMathValue x+  toMathValue (CAS.CASTerm (CAS.CASInteger 1) xs) = Multiply (map toMathValue xs)+  toMathValue (CAS.CASTerm coeff xs) = Multiply (toMathValue coeff : map toMathValue xs) -instance ToMathExpr Integer where-  toMathExpr n | n < 0 = NegativeAtom (show (-n))-  toMathExpr n         = Atom (show n) []+instance ToMathValue Integer where+  toMathValue n | n < 0 = NegativeAtom (show (-n))+  toMathValue n         = Atom (show n) [] -instance {-# OVERLAPPING #-} ToMathExpr (E.SymbolExpr, Integer) where-  toMathExpr (x, 1) = toMathExpr x-  toMathExpr (x, n) = Power (toMathExpr x) (toMathExpr n)+instance {-# OVERLAPPING #-} ToMathValue (CAS.SymbolExpr, Integer) where+  toMathValue (x, 1) = toMathValue x+  toMathValue (x, n) = Power (toMathValue x) (toMathValue n) -instance ToMathExpr E.SymbolExpr where-  toMathExpr (E.Symbol _ (':':':':':':_) []) = Atom "#" []-  toMathExpr (E.Symbol _ s js) = toMathExpr' js (Atom s [])+instance ToMathValue CAS.SymbolExpr where+  toMathValue (CAS.Symbol _ (':':':':':':_) []) = Atom "#" []+  toMathValue (CAS.Symbol _ s js) = toMathValue' js (Atom s [])     where-      toMathExpr' [] acc = acc-      toMathExpr' (E.User x:js) (Partial e ps) =-        toMathExpr' js (Partial e (ps ++ [toMathExpr x]))-      toMathExpr' (E.User x:js) e@Atom{} =-        toMathExpr' js (Partial e [toMathExpr x])-      toMathExpr' (j:js) (Atom e is) =-        toMathExpr' js (Atom e (is ++ [toMathIndex j]))-      toMathExpr' _ _ = undefined -- TODO+      toMathValue' [] acc = acc+      toMathValue' (E.User x:js) (Partial e ps) =+        toMathValue' js (Partial e (ps ++ [toMathValue x]))+      toMathValue' (E.User x:js) e@Atom{} =+        toMathValue' js (Partial e [toMathValue x])+      toMathValue' (j:js) (Atom e is) =+        toMathValue' js (Atom e (is ++ [toMathIndex j]))+      toMathValue' _ _ = undefined -- TODO -  toMathExpr (E.Apply1 fn a1) =-    case toMathExpr fn of-      Atom "^" [] -> Power (toMathExpr fn) (toMathExpr a1)-      _           -> Func (toMathExpr fn) [toMathExpr a1]-  toMathExpr (E.Apply2 fn a1 a2) =-    case toMathExpr fn of-      Atom "^" [] -> Power (toMathExpr a1) (toMathExpr a2)-      _           -> Func (toMathExpr fn) [toMathExpr a1, toMathExpr a2]-  toMathExpr (E.Apply3 fn a1 a2 a3) =-    Func (toMathExpr fn) [toMathExpr a1, toMathExpr a2, toMathExpr a3]-  toMathExpr (E.Apply4 fn a1 a2 a3 a4) =-    Func (toMathExpr fn) [toMathExpr a1, toMathExpr a2, toMathExpr a3, toMathExpr a4]-  toMathExpr (E.Quote mExpr) = Quote (toMathExpr mExpr)-  toMathExpr (E.QuoteFunction whnf) =+  toMathValue (CAS.Apply1 fn a1) =+    case toMathValue fn of+      Atom "^" [] -> Power (toMathValue fn) (toMathValue a1)+      _           -> Func (toMathValue fn) [toMathValue a1]+  toMathValue (CAS.Apply2 fn a1 a2) =+    case toMathValue fn of+      Atom "^" [] -> Power (toMathValue a1) (toMathValue a2)+      _           -> Func (toMathValue fn) [toMathValue a1, toMathValue a2]+  toMathValue (CAS.Apply3 fn a1 a2 a3) =+    Func (toMathValue fn) [toMathValue a1, toMathValue a2, toMathValue a3]+  toMathValue (CAS.Apply4 fn a1 a2 a3 a4) =+    Func (toMathValue fn) [toMathValue a1, toMathValue a2, toMathValue a3, toMathValue a4]+  toMathValue (CAS.Quote mExpr) = Quote (toMathValue mExpr)+  toMathValue (CAS.QuoteFunction whnf) =     case E.prettyFunctionName whnf of       Just name -> Atom name []       Nothing   -> Atom "f" []-  toMathExpr (E.FunctionData (E.SingleTerm 1 [(E.Symbol _ s js, 1)]) _) = toMathExpr' js (Atom s [])+  toMathValue (CAS.FunctionData (CAS.CASPoly [CAS.CASTerm (CAS.CASInteger 1) [(CAS.Symbol _ s js, 1)]]) _) = toMathValue' js (Atom s [])     where-      toMathExpr' [] acc = acc-      toMathExpr' (E.User x:js) (Partial e ps) =-        toMathExpr' js (Partial e (ps ++ [toMathExpr x]))-      toMathExpr' (E.User x:js) e@Atom{} =-        toMathExpr' js (Partial e [toMathExpr x])-      toMathExpr' (j:js) (Atom e is) =-        toMathExpr' js (Atom e (is ++ [toMathIndex j]))-      toMathExpr' _ _ = undefined -- TODO-  toMathExpr (E.FunctionData name _) = toMathExpr name+      toMathValue' [] acc = acc+      toMathValue' (E.User x:js) (Partial e ps) =+        toMathValue' js (Partial e (ps ++ [toMathValue x]))+      toMathValue' (E.User x:js) e@Atom{} =+        toMathValue' js (Partial e [toMathValue x])+      toMathValue' (j:js) (Atom e is) =+        toMathValue' js (Atom e (is ++ [toMathIndex j]))+      toMathValue' _ _ = undefined -- TODO+  toMathValue (CAS.FunctionData name _) = toMathValue name -toMathIndex :: ToMathExpr a => E.Index a -> MathIndex-toMathIndex (E.Sub x) = Sub (toMathExpr x)-toMathIndex (E.Sup x) = Super (toMathExpr x)+toMathIndex :: ToMathValue a => E.Index a -> MathIndex+toMathIndex (E.Sub x) = Sub (toMathValue x)+toMathIndex (E.Sup x) = Super (toMathValue x) toMathIndex _         = undefined -- TODO  --@@ -147,50 +149,50 @@ symbol :: Parser Char symbol = oneOf "!$%&*+-/:<=>?@#" -parseAtom :: Parser MathExpr+parseAtom :: Parser MathValue parseAtom = Atom <$> ((:) <$> (letter <|> symbol <|> digit) <*> many (letter <|> digit <|> symbol)) <*> many parseScript -parseAtom' :: Parser MathExpr+parseAtom' :: Parser MathValue parseAtom' = flip Atom [] <$> ((:) <$> (letter <|> symbol <|> digit) <*> many (letter <|> digit <|> symbol)) -parsePartial :: Parser MathExpr+parsePartial :: Parser MathValue parsePartial = Partial <$> parseAtom <*> many1 (char '|' >> parseAtom) -parseNegativeAtom :: Parser MathExpr+parseNegativeAtom :: Parser MathValue parseNegativeAtom = char '-' >> NegativeAtom <$> ((:) <$> (letter <|> symbol <|> digit) <*> many (letter <|> digit <|> symbol)) -parseList :: Parser [MathExpr]+parseList :: Parser [MathValue] parseList = sepEndBy parseExpr spaces  parseScript :: Parser MathIndex parseScript = Sub <$> (char '_' >> parseAtom')               <|> Super <$> (char '~' >> parseAtom') -parsePlus :: Parser MathExpr+parsePlus :: Parser MathValue parsePlus = try (string "(+") >> spaces >> Plus <$> parseList <* char ')' -parseMultiply :: Parser MathExpr+parseMultiply :: Parser MathValue parseMultiply = try (string "(*") >> spaces >> Multiply <$> parseList <* char ')' -parseDiv :: Parser MathExpr+parseDiv :: Parser MathValue parseDiv = try (string "(/") >> spaces >> Div <$> parseExpr <*> (spaces >> parseExpr) <* char ')' -parseFunction :: Parser MathExpr+parseFunction :: Parser MathValue parseFunction = char '(' >> Func <$> parseAtom <* spaces <*> parseList <* char ')' -parseTensor :: Parser MathExpr+parseTensor :: Parser MathValue parseTensor = string "[|" >> spaces0 >> Tensor <$> parseList <* spaces0 <* string "|]" <*> many parseScript -parseTuple :: Parser MathExpr+parseTuple :: Parser MathValue parseTuple = char '[' >> Tuple <$> parseList <* char ']' -parseCollection :: Parser MathExpr+parseCollection :: Parser MathValue parseCollection = char '{' >> Collection <$> parseList <* char '}' -parseQuote :: Parser MathExpr+parseQuote :: Parser MathValue parseQuote = char '\'' >> Quote <$> parseExpr' -parseExpr' :: Parser MathExpr+parseExpr' :: Parser MathValue parseExpr' = parseNegativeAtom          <|> try parsePartial          <|> parseAtom@@ -203,7 +205,7 @@          <|> try parseTuple          <|> try parseCollection -parseExpr :: Parser MathExpr+parseExpr :: Parser MathValue parseExpr = do   x <- parseExpr'   option x $ Power x <$> try (char '^' >> parseExpr')
hs-src/Language/Egison/PrettyMath/AsciiMath.hs view
@@ -4,54 +4,54 @@ -}  module Language.Egison.PrettyMath.AsciiMath-  ( showMathExpr+  ( showMathValue   ) where  import           Data.List                      (intercalate)  import           Language.Egison.PrettyMath.AST -showMathExpr :: MathExpr -> String-showMathExpr (Atom func []) = func-showMathExpr (NegativeAtom func) = "-" ++ func-showMathExpr (Plus []) = ""-showMathExpr (Plus (x:xs)) = showMathExpr x ++ showMathExprForPlus xs+showMathValue :: MathValue -> String+showMathValue (Atom func []) = func+showMathValue (NegativeAtom func) = "-" ++ func+showMathValue (Plus []) = ""+showMathValue (Plus (x:xs)) = showMathValue x ++ showMathValueForPlus xs  where-  showMathExprForPlus :: [MathExpr] -> String-  showMathExprForPlus []                                  = ""-  showMathExprForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathExpr (Multiply ys) ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathExpr (Multiply (Atom a []:ys)) ++ " " ++ showMathExprForPlus xs-  showMathExprForPlus (x:xs)                              = " + " ++ showMathExpr x ++ showMathExprForPlus xs-showMathExpr (Multiply []) = ""-showMathExpr (Multiply [x]) = showMathExpr x-showMathExpr (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathExpr (Multiply xs)-showMathExpr (Multiply (x:xs)) = showMathExpr' x ++ " " ++ showMathExpr (Multiply xs)-showMathExpr (Div x y) = "frac{" ++ showMathExpr x ++ "}{" ++ showMathExpr y ++ "}"-showMathExpr (Power lv1 lv2) = showMathExpr lv1 ++ "^" ++ showMathExpr lv2-showMathExpr (Func (Atom "sqrt" []) [x]) = "sqrt " ++ showMathExpr x-showMathExpr (Func (Atom "rt" []) [x, y]) = "root " ++ showMathExpr x ++ " " ++ showMathExpr y-showMathExpr (Func (Atom "exp" []) [x]) = "e^(" ++ showMathExpr x ++ ")"-showMathExpr (Func f lvs) = showMathExpr f ++ "(" ++ showMathExprArg lvs ++ ")"-showMathExpr (Tensor lvs mis)-  | null mis = "(" ++ showMathExprArg lvs ++ ")"-  | not (any isSub mis) = "(" ++ showMathExprArg lvs ++ ")^(" ++ showMathExprIndices mis ++ ")"-  | all isSub mis = "(" ++ showMathExprArg lvs ++ ")_(" ++ showMathExprIndices mis ++ ")"-  | otherwise = "(" ++ showMathExprArg lvs ++ ")_(" ++ showMathExprIndices (filter isSub mis) ++ ")^(" ++ showMathExprIndices (filter (not . isSub) mis) ++ ")"-showMathExpr (Tuple lvs) = "(" ++ showMathExprArg lvs ++ ")"-showMathExpr (Collection lvs) = "{" ++ showMathExprArg lvs ++ "}"+  showMathValueForPlus :: [MathValue] -> String+  showMathValueForPlus []                                  = ""+  showMathValueForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathValue (Multiply ys) ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathValue (Multiply (Atom a []:ys)) ++ " " ++ showMathValueForPlus xs+  showMathValueForPlus (x:xs)                              = " + " ++ showMathValue x ++ showMathValueForPlus xs+showMathValue (Multiply []) = ""+showMathValue (Multiply [x]) = showMathValue x+showMathValue (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathValue (Multiply xs)+showMathValue (Multiply (x:xs)) = showMathValue' x ++ " " ++ showMathValue (Multiply xs)+showMathValue (Div x y) = "frac{" ++ showMathValue x ++ "}{" ++ showMathValue y ++ "}"+showMathValue (Power lv1 lv2) = showMathValue' lv1 ++ "^(" ++ showMathValue lv2 ++ ")"+showMathValue (Func (Atom "sqrt" []) [x]) = "sqrt " ++ showMathValue x+showMathValue (Func (Atom "rt" []) [x, y]) = "root " ++ showMathValue x ++ " " ++ showMathValue y+showMathValue (Func (Atom "exp" []) [x]) = "e^(" ++ showMathValue x ++ ")"+showMathValue (Func f lvs) = showMathValue f ++ "(" ++ showMathValueArg lvs ++ ")"+showMathValue (Tensor lvs mis)+  | null mis = "(" ++ showMathValueArg lvs ++ ")"+  | not (any isSub mis) = "(" ++ showMathValueArg lvs ++ ")^(" ++ showMathValueIndices mis ++ ")"+  | all isSub mis = "(" ++ showMathValueArg lvs ++ ")_(" ++ showMathValueIndices mis ++ ")"+  | otherwise = "(" ++ showMathValueArg lvs ++ ")_(" ++ showMathValueIndices (filter isSub mis) ++ ")^(" ++ showMathValueIndices (filter (not . isSub) mis) ++ ")"+showMathValue (Tuple lvs) = "(" ++ showMathValueArg lvs ++ ")"+showMathValue (Collection lvs) = "{" ++ showMathValueArg lvs ++ "}" -showMathExpr' :: MathExpr -> String-showMathExpr' (Plus lvs) = "(" ++ showMathExpr (Plus lvs) ++ ")"-showMathExpr' val        = showMathExpr val+showMathValue' :: MathValue -> String+showMathValue' (Plus lvs) = "(" ++ showMathValue (Plus lvs) ++ ")"+showMathValue' val        = showMathValue val -showMathExprArg :: [MathExpr] -> String-showMathExprArg exprs = intercalate ", " $ map showMathExpr exprs+showMathValueArg :: [MathValue] -> String+showMathValueArg exprs = intercalate ", " $ map showMathValue exprs -showMathExprIndices :: [MathIndex] -> String-showMathExprIndices []  = error "unreachable"-showMathExprIndices lvs = concatMap showMathIndex lvs+showMathValueIndices :: [MathIndex] -> String+showMathValueIndices []  = error "unreachable"+showMathValueIndices lvs = concatMap showMathIndex lvs  showMathIndex :: MathIndex -> String-showMathIndex (Super a) = showMathExpr a-showMathIndex (Sub a)   = showMathExpr a+showMathIndex (Super a) = showMathValue a+showMathIndex (Sub a)   = showMathValue a
hs-src/Language/Egison/PrettyMath/Latex.hs view
@@ -4,79 +4,91 @@ -}  module Language.Egison.PrettyMath.Latex-  ( showMathExpr+  ( showMathValue   ) where  import           Data.List                      (intercalate)  import           Language.Egison.PrettyMath.AST -showMathExpr :: MathExpr -> String-showMathExpr (Atom a []) = a-showMathExpr (Atom a xs) = a ++ showMathExprScript xs-showMathExpr (Partial f xs) = "\\frac{" ++ convertToPartial (f, length xs) ++ "}{" ++ showPartial xs ++ "}"+showMathValue :: MathValue -> String+showMathValue (Atom a []) = a+showMathValue (Atom a xs) = a ++ showMathValueScript xs+showMathValue (Partial f xs) = "\\frac{" ++ convertToPartial (f, length xs) ++ "}{" ++ showPartial xs ++ "}"  where-  showPartial :: [MathExpr] -> String-  showPartial xs = let lx = elemCount xs in convertToPartial2 (head lx) ++ foldr (\x acc -> " " ++ convertToPartial2 x ++ acc) "" (tail lx)+  showPartial :: [MathValue] -> String+  showPartial xs = case elemCount xs of+    []      -> ""+    (l:lx)  -> convertToPartial2 l ++ foldr (\x acc -> " " ++ convertToPartial2 x ++ acc) "" lx -  convertToPartial :: (MathExpr, Int) -> String-  convertToPartial (x, 1) = "\\partial " ++ showMathExpr x-  convertToPartial (x, n) = "\\partial^" ++ show n ++ " " ++ showMathExpr x+  convertToPartial :: (MathValue, Int) -> String+  convertToPartial (x, 1) = "\\partial " ++ showMathValue x+  convertToPartial (x, n) = "\\partial^" ++ show n ++ " " ++ showMathValue x -  convertToPartial2 :: (MathExpr, Int) -> String-  convertToPartial2 (x, 1) = "\\partial " ++ showMathExpr x-  convertToPartial2 (x, n) = "\\partial " ++ showMathExpr x ++ "^"  ++ show n-showMathExpr (NegativeAtom a) = "-" ++ a-showMathExpr (Plus []) = ""-showMathExpr (Plus (x:xs)) = showMathExpr x ++ showMathExprForPlus xs+  convertToPartial2 :: (MathValue, Int) -> String+  convertToPartial2 (x, 1) = "\\partial " ++ showMathValue x+  convertToPartial2 (x, n) = "\\partial " ++ showMathValue x ++ "^"  ++ show n+showMathValue (NegativeAtom a) = "-" ++ a+showMathValue (Plus []) = ""+showMathValue (Plus (x:xs)) = showMathValue x ++ showMathValueForPlus xs  where-  showMathExprForPlus :: [MathExpr] -> String-  showMathExprForPlus []                                  = ""-  showMathExprForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathExpr (Multiply ys) ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathExpr (Multiply (Atom a []:ys)) ++ showMathExprForPlus xs-  showMathExprForPlus (x:xs)                              = " + " ++  showMathExpr x ++ showMathExprForPlus xs-showMathExpr (Multiply []) = ""-showMathExpr (Multiply [x]) = showMathExpr x-showMathExpr (Multiply (Atom "1" []:xs)) = showMathExpr (Multiply xs)-showMathExpr (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathExpr (Multiply xs)-showMathExpr (Multiply (x:xs)) = showMathExpr' x ++ " " ++ showMathExpr (Multiply xs)-showMathExpr (Div x y) = "\\frac{" ++ showMathExpr x ++ "}{" ++ showMathExpr y ++ "}"-showMathExpr (Power lv1 lv2) = showMathExpr lv1 ++ "^" ++ showMathExpr lv2-showMathExpr (Func (Atom "sqrt" []) [x]) = "\\sqrt{" ++ showMathExpr x ++ "}"-showMathExpr (Func (Atom "rt" []) [x, y]) = "\\sqrt[" ++ showMathExpr x ++ "]{" ++ showMathExpr y ++ "}"-showMathExpr (Func (Atom "exp" []) [x]) = "e^{" ++ showMathExpr x ++ "}"-showMathExpr (Func f xs) = showMathExpr f ++ "(" ++ showMathExprArg xs ", " ++ ")"-showMathExpr (Tensor xs mis) = "\\begin{pmatrix} " ++ showMathExprVectors xs ++ "\\end{pmatrix}" ++ showMathExprScript mis-showMathExpr (Tuple xs) = "(" ++ showMathExprArg xs ", " ++ ")"-showMathExpr (Collection xs) = "\\{" ++ showMathExprArg xs ", " ++ "\\}"-showMathExpr (Quote x) = "(" ++ showMathExpr x ++ ")"+  showMathValueForPlus :: [MathValue] -> String+  showMathValueForPlus []                                  = ""+  showMathValueForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathValue (Multiply ys) ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathValue (Multiply (Atom a []:ys)) ++ showMathValueForPlus xs+  showMathValueForPlus (x:xs)                              = " + " ++  showMathValue x ++ showMathValueForPlus xs+showMathValue (Multiply []) = ""+showMathValue (Multiply [x]) = showMathValue x+showMathValue (Multiply (Atom "1" []:xs)) = showMathValue (Multiply xs)+showMathValue (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathValue (Multiply xs)+showMathValue (Multiply (x:xs)) = showMathValue' x ++ " " ++ showMathValue (Multiply xs)+showMathValue (Div x y) = "\\frac{" ++ showMathValue x ++ "}{" ++ showMathValue y ++ "}"+showMathValue (Power lv1 lv2) = showMathValue' lv1 ++ "^{" ++ showMathValue lv2 ++ "}"+showMathValue (Func (Atom "sqrt" []) [x]) = "\\sqrt{" ++ showMathValue x ++ "}"+showMathValue (Func (Atom "rt" []) [x, y]) = "\\sqrt[" ++ showMathValue x ++ "]{" ++ showMathValue y ++ "}"+showMathValue (Func (Atom "exp" []) [x]) = "e^{" ++ showMathValue x ++ "}"+showMathValue (Func (Atom fn []) xs) | fn `elem` latexMathOperators =+  "\\" ++ fn ++ "(" ++ showMathValueArg xs ", " ++ ")"+showMathValue (Func f xs) = showMathValue f ++ "(" ++ showMathValueArg xs ", " ++ ")"+showMathValue (Tensor xs mis) = "\\begin{pmatrix} " ++ showMathValueVectors xs ++ "\\end{pmatrix}" ++ showMathValueScript mis+showMathValue (Tuple xs) = "(" ++ showMathValueArg xs ", " ++ ")"+showMathValue (Collection xs) = "\\{" ++ showMathValueArg xs ", " ++ "\\}"+showMathValue (Quote x) = "(" ++ showMathValue x ++ ")" -showMathExpr' :: MathExpr -> String-showMathExpr' (Plus xs) = "(" ++ showMathExpr (Plus xs) ++ ")"-showMathExpr' x         = showMathExpr x+showMathValue' :: MathValue -> String+showMathValue' (Plus xs) = "(" ++ showMathValue (Plus xs) ++ ")"+showMathValue' x         = showMathValue x -showMathExprArg :: [MathExpr] -> String -> String-showMathExprArg exprs sep = intercalate sep $ map showMathExpr exprs+showMathValueArg :: [MathValue] -> String -> String+showMathValueArg exprs sep = intercalate sep $ map showMathValue exprs -showMathExprSuper :: MathIndex -> String-showMathExprSuper (Super (Atom "#" [])) = "\\#"-showMathExprSuper (Super x)             = showMathExpr x-showMathExprSuper (Sub _)               = "\\;"+-- Standard LaTeX math operators that must be typeset upright with a+-- backslash-prefixed control sequence (e.g. \sin instead of an italic "sin").+latexMathOperators :: [String]+latexMathOperators =+  [ "sin", "cos", "tan", "cot", "sec", "csc"+  , "sinh", "cosh", "tanh", "coth"+  , "log", "ln", "arcsin", "arccos", "arctan" ] -showMathExprSub :: MathIndex -> String-showMathExprSub (Sub (Atom "#" [])) = "\\#"-showMathExprSub (Sub x)             = showMathExpr x-showMathExprSub (Super _)           = "\\;"+showMathValueSuper :: MathIndex -> String+showMathValueSuper (Super (Atom "#" [])) = "\\#"+showMathValueSuper (Super x)             = showMathValue x+showMathValueSuper (Sub _)               = "\\;" -showMathExprScript :: [MathIndex] -> String-showMathExprScript [] = ""-showMathExprScript is = "_{" ++ concatMap showMathExprSub is ++ "}^{" ++ concatMap showMathExprSuper is ++ "}"+showMathValueSub :: MathIndex -> String+showMathValueSub (Sub (Atom "#" [])) = "\\#"+showMathValueSub (Sub x)             = showMathValue x+showMathValueSub (Super _)           = "\\;" -showMathExprVectors :: [MathExpr] -> String-showMathExprVectors []                = ""-showMathExprVectors (Tensor lvs []:r) = showMathExprArg lvs " & " ++ " \\\\ " ++ showMathExprVectors r-showMathExprVectors lvs               = showMathExprArg lvs " \\\\ " ++ "\\\\ "+showMathValueScript :: [MathIndex] -> String+showMathValueScript [] = ""+showMathValueScript is = "_{" ++ concatMap showMathValueSub is ++ "}^{" ++ concatMap showMathValueSuper is ++ "}"++showMathValueVectors :: [MathValue] -> String+showMathValueVectors []                = ""+showMathValueVectors (Tensor lvs []:r) = showMathValueArg lvs " & " ++ " \\\\ " ++ showMathValueVectors r+showMathValueVectors lvs               = showMathValueArg lvs " \\\\ " ++ "\\\\ "  elemCount :: Eq a => [a] -> [(a, Int)] elemCount []     = []
hs-src/Language/Egison/PrettyMath/Mathematica.hs view
@@ -4,64 +4,64 @@ -}  module Language.Egison.PrettyMath.Mathematica-  ( showMathExpr+  ( showMathValue   ) where  import           Data.List                      (intercalate)  import           Language.Egison.PrettyMath.AST -showMathExpr :: MathExpr -> String-showMathExpr (Atom a []) = a-showMathExpr (Atom a xs) = a ++ showMathExprIndices xs-showMathExpr (Partial f xs) = showMathExpr f ++ "_" ++ showMathExprs "_" xs-showMathExpr (NegativeAtom a) = "-" ++ a-showMathExpr (Plus []) = ""-showMathExpr (Plus (x:xs)) = showMathExpr x ++ showMathExprForPlus xs+showMathValue :: MathValue -> String+showMathValue (Atom a []) = a+showMathValue (Atom a xs) = a ++ showMathValueIndices xs+showMathValue (Partial f xs) = showMathValue f ++ "_" ++ showMathValues "_" xs+showMathValue (NegativeAtom a) = "-" ++ a+showMathValue (Plus []) = ""+showMathValue (Plus (x:xs)) = showMathValue x ++ showMathValueForPlus xs  where-  showMathExprForPlus :: [MathExpr] -> String-  showMathExprForPlus []                                  = ""-  showMathExprForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathExpr (Multiply ys) ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathExpr (Multiply (Atom a []:ys)) ++ showMathExprForPlus xs-  showMathExprForPlus (x:xs)                              = " + " ++  showMathExpr x ++ showMathExprForPlus xs-showMathExpr (Multiply []) = ""-showMathExpr (Multiply [x]) = showMathExpr x-showMathExpr (Multiply (Atom "1" []:xs)) = showMathExpr (Multiply xs)-showMathExpr (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathExpr (Multiply xs)-showMathExpr (Multiply (x:xs)) = showMathExpr' x ++ " " ++ showMathExpr (Multiply xs)-showMathExpr (Div x y) = addBracket x ++ "/" ++ addBracket y+  showMathValueForPlus :: [MathValue] -> String+  showMathValueForPlus []                                  = ""+  showMathValueForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathValue (Multiply ys) ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathValue (Multiply (Atom a []:ys)) ++ showMathValueForPlus xs+  showMathValueForPlus (x:xs)                              = " + " ++  showMathValue x ++ showMathValueForPlus xs+showMathValue (Multiply []) = ""+showMathValue (Multiply [x]) = showMathValue x+showMathValue (Multiply (Atom "1" []:xs)) = showMathValue (Multiply xs)+showMathValue (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathValue (Multiply xs)+showMathValue (Multiply (x:xs)) = showMathValue' x ++ " " ++ showMathValue (Multiply xs)+showMathValue (Div x y) = addBracket x ++ "/" ++ addBracket y  where-   addBracket x@(Atom _ []) = showMathExpr x-   addBracket x             = "(" ++ showMathExpr x ++ ")"-showMathExpr (Power lv1 lv2) = showMathExpr lv1 ++ "^" ++ showMathExpr lv2-showMathExpr (Func (Atom "sqrt" []) [x]) = "Sqrt[" ++ showMathExpr x ++ "]"-showMathExpr (Func (Atom "rt" []) [x, y]) = "Surd[" ++ showMathExpr x ++ "," ++ showMathExpr y ++ "]"-showMathExpr (Func (Atom "exp" []) [x])= "e^(" ++ showMathExpr x ++ ")"-showMathExpr (Func f xs) = showMathExpr f ++ "(" ++ showMathExprArg xs ++ ")"-showMathExpr (Tensor lvs mis)-  | null mis = "{" ++ showMathExprArg lvs ++ "}"-  | not (any isSub mis) = "{" ++ showMathExprArg lvs ++ "}^(" ++ showMathExprIndices mis ++ ")"-  | all isSub mis = "{" ++ showMathExprArg lvs ++ "}_(" ++ showMathExprIndices mis ++ ")"-  | otherwise = "{" ++ showMathExprArg lvs ++ "}_(" ++ showMathExprIndices (filter isSub mis) ++ ")^(" ++ showMathExprIndices (filter (not . isSub) mis) ++ ")"-showMathExpr (Tuple xs) = "(" ++ showMathExprArg xs ++ ")"-showMathExpr (Collection xs) = "{" ++ showMathExprArg xs ++ "}"-showMathExpr (Quote x) = "(" ++ showMathExpr x ++ ")"+   addBracket x@(Atom _ []) = showMathValue x+   addBracket x             = "(" ++ showMathValue x ++ ")"+showMathValue (Power lv1 lv2) = showMathValue lv1 ++ "^" ++ showMathValue lv2+showMathValue (Func (Atom "sqrt" []) [x]) = "Sqrt[" ++ showMathValue x ++ "]"+showMathValue (Func (Atom "rt" []) [x, y]) = "Surd[" ++ showMathValue x ++ "," ++ showMathValue y ++ "]"+showMathValue (Func (Atom "exp" []) [x])= "e^(" ++ showMathValue x ++ ")"+showMathValue (Func f xs) = showMathValue f ++ "(" ++ showMathValueArg xs ++ ")"+showMathValue (Tensor lvs mis)+  | null mis = "{" ++ showMathValueArg lvs ++ "}"+  | not (any isSub mis) = "{" ++ showMathValueArg lvs ++ "}^(" ++ showMathValueIndices mis ++ ")"+  | all isSub mis = "{" ++ showMathValueArg lvs ++ "}_(" ++ showMathValueIndices mis ++ ")"+  | otherwise = "{" ++ showMathValueArg lvs ++ "}_(" ++ showMathValueIndices (filter isSub mis) ++ ")^(" ++ showMathValueIndices (filter (not . isSub) mis) ++ ")"+showMathValue (Tuple xs) = "(" ++ showMathValueArg xs ++ ")"+showMathValue (Collection xs) = "{" ++ showMathValueArg xs ++ "}"+showMathValue (Quote x) = "(" ++ showMathValue x ++ ")" -showMathExpr' :: MathExpr -> String-showMathExpr' (Plus xs) = "(" ++ showMathExpr (Plus xs) ++ ")"-showMathExpr' x         = showMathExpr x+showMathValue' :: MathValue -> String+showMathValue' (Plus xs) = "(" ++ showMathValue (Plus xs) ++ ")"+showMathValue' x         = showMathValue x -showMathExprs :: String -> [MathExpr] -> String-showMathExprs sep exprs = intercalate sep $ map showMathExpr exprs+showMathValues :: String -> [MathValue] -> String+showMathValues sep exprs = intercalate sep $ map showMathValue exprs -showMathExprArg :: [MathExpr] -> String-showMathExprArg = showMathExprs ", "+showMathValueArg :: [MathValue] -> String+showMathValueArg = showMathValues ", " -showMathExprIndices :: [MathIndex] -> String-showMathExprIndices []  = error "unreachable"-showMathExprIndices lvs = concatMap showMathIndex lvs+showMathValueIndices :: [MathIndex] -> String+showMathValueIndices []  = error "unreachable"+showMathValueIndices lvs = concatMap showMathIndex lvs  showMathIndex :: MathIndex -> String-showMathIndex (Super a) = showMathExpr a-showMathIndex (Sub a)   = showMathExpr a+showMathIndex (Super a) = showMathValue a+showMathIndex (Sub a)   = showMathValue a
hs-src/Language/Egison/PrettyMath/Maxima.hs view
@@ -4,49 +4,49 @@ -}  module Language.Egison.PrettyMath.Maxima-  ( showMathExpr+  ( showMathValue   ) where  import           Language.Egison.PrettyMath.AST -showMathExpr :: MathExpr -> String-showMathExpr (Atom a []) = a-showMathExpr (Partial _ _) = "undefined"-showMathExpr (NegativeAtom a) = "-" ++ a-showMathExpr (Plus []) = ""-showMathExpr (Plus (x:xs)) = showMathExpr x ++ showMathExprForPlus xs+showMathValue :: MathValue -> String+showMathValue (Atom a []) = a+showMathValue (Partial _ _) = "undefined"+showMathValue (NegativeAtom a) = "-" ++ a+showMathValue (Plus []) = ""+showMathValue (Plus (x:xs)) = showMathValue x ++ showMathValueForPlus xs  where-  showMathExprForPlus :: [MathExpr] -> String-  showMathExprForPlus []                                  = ""-  showMathExprForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathExpr (Multiply ys) ++ showMathExprForPlus xs-  showMathExprForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathExpr (Multiply (Atom a []:ys)) ++ showMathExprForPlus xs-  showMathExprForPlus (x:xs)                              = " + " ++  showMathExpr x ++ showMathExprForPlus xs-showMathExpr (Multiply []) = ""-showMathExpr (Multiply [x]) = showMathExpr x-showMathExpr (Multiply (Atom "1" []:xs)) = showMathExpr (Multiply xs)-showMathExpr (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathExpr (Multiply xs)-showMathExpr (Multiply (x:xs)) = showMathExpr' x ++ " * " ++ showMathExpr (Multiply xs)-showMathExpr (Div x y) = addBracket x ++ "/" ++ addBracket y+  showMathValueForPlus :: [MathValue] -> String+  showMathValueForPlus []                                  = ""+  showMathValueForPlus (NegativeAtom a:xs)                 = " - " ++ a ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom "1":ys):xs) = " - " ++ showMathValue (Multiply ys) ++ showMathValueForPlus xs+  showMathValueForPlus (Multiply (NegativeAtom a:ys):xs)   = " - " ++ showMathValue (Multiply (Atom a []:ys)) ++ showMathValueForPlus xs+  showMathValueForPlus (x:xs)                              = " + " ++  showMathValue x ++ showMathValueForPlus xs+showMathValue (Multiply []) = ""+showMathValue (Multiply [x]) = showMathValue x+showMathValue (Multiply (Atom "1" []:xs)) = showMathValue (Multiply xs)+showMathValue (Multiply (NegativeAtom "1":xs)) = "-" ++ showMathValue (Multiply xs)+showMathValue (Multiply (x:xs)) = showMathValue' x ++ " * " ++ showMathValue (Multiply xs)+showMathValue (Div x y) = addBracket x ++ "/" ++ addBracket y  where-   addBracket x@(Atom _ []) = showMathExpr x-   addBracket x             = "(" ++ showMathExpr x ++ ")"-showMathExpr (Power lv1 lv2) = showMathExpr lv1 ++ "^" ++ showMathExpr lv2-showMathExpr (Func (Atom "sqrt" []) [x]) = "sqrt(" ++ showMathExpr x ++ ")"-showMathExpr (Func (Atom "rt" []) [x, y]) = showMathExpr y ++ "^(1/" ++ showMathExpr x ++ ")"-showMathExpr (Func (Atom "exp" []) [x]) = "exp(" ++ showMathExpr x ++ ")"-showMathExpr (Func f xs) = showMathExpr f ++ "(" ++ showMathExprArg xs ++ ")"-showMathExpr (Tensor _ _) = "undefined"-showMathExpr (Tuple _) = "undefined"-showMathExpr (Collection xs) = "[" ++ showMathExprArg xs ++ "]"-showMathExpr (Quote x) = "(" ++ showMathExpr x ++ ")"+   addBracket x@(Atom _ []) = showMathValue x+   addBracket x             = "(" ++ showMathValue x ++ ")"+showMathValue (Power lv1 lv2) = showMathValue lv1 ++ "^" ++ showMathValue lv2+showMathValue (Func (Atom "sqrt" []) [x]) = "sqrt(" ++ showMathValue x ++ ")"+showMathValue (Func (Atom "rt" []) [x, y]) = showMathValue y ++ "^(1/" ++ showMathValue x ++ ")"+showMathValue (Func (Atom "exp" []) [x]) = "exp(" ++ showMathValue x ++ ")"+showMathValue (Func f xs) = showMathValue f ++ "(" ++ showMathValueArg xs ++ ")"+showMathValue (Tensor _ _) = "undefined"+showMathValue (Tuple _) = "undefined"+showMathValue (Collection xs) = "[" ++ showMathValueArg xs ++ "]"+showMathValue (Quote x) = "(" ++ showMathValue x ++ ")" -showMathExpr' :: MathExpr -> String-showMathExpr' x@(Plus _) = "(" ++ showMathExpr x ++ ")"-showMathExpr' x          = showMathExpr x+showMathValue' :: MathValue -> String+showMathValue' x@(Plus _) = "(" ++ showMathValue x ++ ")"+showMathValue' x          = showMathValue x -showMathExprArg :: [MathExpr] -> String-showMathExprArg []            = ""-showMathExprArg [Tensor _ []] = "undefined"-showMathExprArg [a]           = showMathExpr a-showMathExprArg lvs           = showMathExpr (head lvs) ++ ", " ++ showMathExprArg (tail lvs)+showMathValueArg :: [MathValue] -> String+showMathValueArg []            = ""+showMathValueArg [Tensor _ []] = "undefined"+showMathValueArg [a]           = showMathValue a+showMathValueArg (lv:lvs)      = showMathValue lv ++ ", " ++ showMathValueArg lvs
hs-src/Language/Egison/Primitives.hs view
@@ -12,24 +12,32 @@   , primitiveEnvNoIO   ) where -import           Control.Monad                     (forM)+import           Control.Monad                     (foldM, forM)+import           Control.Monad.Except              (throwError) import           Control.Monad.IO.Class            (liftIO)  import           Data.IORef-import           Data.List                         (lookup) import           Data.Foldable                     (toList)+import           Data.Monoid                       (Any (..))  import qualified Data.Sequence                     as Sq+import qualified Data.Set                          as Set+import qualified Data.Text                         as T import qualified Data.Vector                       as V   {--  -- for 'egison-sqlite' import qualified Database.SQLite3 as SQLite  --}  -- for 'egison-sqlite' +import           Language.Egison.Core              (applyRef) import           Language.Egison.Data import           Language.Egison.Data.Collection   (makeICollection)-import           Language.Egison.IExpr             (Index (..), stringToVar)-import           Language.Egison.Math+import           Language.Egison.Data.Utils        (newEvaluatedObjectRef)+import           Language.Egison.EvalState         (getReductionRulesCount, getDerivativeRulesCount,+                                                    getReductionRuleNames, getDerivativeRuleNames,+                                                    getAutoRuleTriggers)+import           Language.Egison.IExpr             (Index (..), Var (..), stringToVar)+import qualified Language.Egison.Math.CAS as CAS import           Language.Egison.Primitives.Arith import           Language.Egison.Primitives.IO import           Language.Egison.Primitives.String@@ -77,12 +85,42 @@          , ("assert",      assert)         , ("assertEqual", assertEqual)-        +         , ("sortWithSign", sortWithSign)         , ("updateFunctionArgs", updateFunctionArgs)+        , ("functionSymbol", functionSymbol)+        , ("symbolIndices", symbolIndices)+        , ("requireAnalyticDerivative", requireAnalyticDerivative)+        , ("quoteScalar", quoteScalar)+        , ("mathFunctionName", mathFunctionName)+        , ("casTerms", casTermsPrim)+        , ("casFromTerms", casFromTermsPrim)+        , ("termCoeff", termCoeffPrim)+        , ("termMonomial", termMonomialPrim)+        , ("typeOf", typeOfPrim)+        , ("inspect", inspectPrim)+        , ("casQuotientCast", casQuotientCastPrim)+        , ("differentialClosed", differentialClosedPrim)+        , ("isInPolyAtoms", isInPolyAtomsPrim)+        , ("isPureInteger", isPureIntegerPrim)+        , ("isPureFraction", isPureFractionPrim)+        , ("numReductionRules", numReductionRulesPrim)+        , ("numDerivativeRules", numDerivativeRulesPrim)+        , ("ruleNames", ruleNamesPrim)+        , ("derivativeNames", derivativeNamesPrim)+        , ("hasReductionRule", hasReductionRulePrim)+        , ("hasDerivativeRule", hasDerivativeRulePrim)+        , ("containsAnySymbol", containsAnySymbolPrim)+        , ("iterateRulesCAS", iterateRulesCASPrim)+        , ("casContainsAnySymbol", casContainsAnySymbolPrim)+        , ("applyTermRule", applyTermRulePrim)+        , ("mapPolyAll", mapPolyPrim)+        , ("mapTermAll", mapTermPrim)+        , ("mapFracAll", mapFracPrim)         ]       lazyPrimitives =         [ ("tensorShape", tensorShape')+        , ("tensorIndices", tensorIndices')         , ("tensorToList", tensorToList')         , ("dfOrder", dfOrder')         ]@@ -100,6 +138,34 @@     return . Value . Collection . Sq.fromList $ map toEgison ns   tensorShape'' _ = return . Value . Collection $ Sq.fromList [] +tensorIndices' :: String -> LazyPrimitiveFunc+tensorIndices' = lazyOneArg tensorIndices''+ where+  tensorIndices'' (Value (TensorData (Tensor _ _ is))) =+    Value . Collection . Sq.fromList <$> publicTensorIndices is+  tensorIndices'' (ITensor (Tensor _ _ is)) =+    Value . Collection . Sq.fromList <$> publicTensorIndices is+  tensorIndices'' _ = return . Value . Collection $ Sq.fromList []++  -- DF indices are evaluator bookkeeping for anonymous dimensions.  Their+  -- public representation is the gap between tensorShape and tensorIndices.+  -- Multi-indices are expanded by desugaring and must not survive in a tensor.+  publicTensorIndices :: [Index EgisonValue] -> EvalM [EgisonValue]+  publicTensorIndices [] = return []+  publicTensorIndices (Sub i : is) =+    (InductiveData "SubIndex" [i] :) <$> publicTensorIndices is+  publicTensorIndices (Sup i : is) =+    (InductiveData "SupIndex" [i] :) <$> publicTensorIndices is+  publicTensorIndices (SupSub i : is) =+    (InductiveData "DiagIndex" [i] :) <$> publicTensorIndices is+  publicTensorIndices (User i : is) =+    (InductiveData "UserIndex" [i] :) <$> publicTensorIndices is+  publicTensorIndices (DF _ _ : is) = publicTensorIndices is+  publicTensorIndices (MultiSub {} : _) =+    throwErrorWithTrace (EgisonBug "tensorIndices encountered an internal multi-subscript")+  publicTensorIndices (MultiSup {} : _) =+    throwErrorWithTrace (EgisonBug "tensorIndices encountered an internal multi-superscript")+ tensorToList' :: String -> LazyPrimitiveFunc tensorToList' = lazyOneArg tensorToList''  where@@ -122,32 +188,751 @@ addSubscript :: String -> PrimitiveFunc addSubscript = twoArgs $ \fn sub ->   case (fn, sub) of-    (ScalarData (SingleSymbol (Symbol id name is)), ScalarData s@(SingleSymbol (Symbol _ _ []))) ->-      return (ScalarData (SingleSymbol (Symbol id name (is ++ [Sub s]))))-    (ScalarData (SingleSymbol (Symbol id name is)), ScalarData s@(SingleTerm _ [])) ->-      return (ScalarData (SingleSymbol (Symbol id name (is ++ [Sub s]))))+    (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]]),+     CASData s@(CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol _ _ [], 1)]])) ->+      return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (is ++ [Sub s]), 1)]]+    (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]]),+     CASData s@(CASPoly [CASTerm (CASInteger _) []])) ->+      return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (is ++ [Sub s]), 1)]]+    (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]]),+     CASData s@(CASInteger _)) ->+      let s' = CASPoly [CASTerm s []]+      in return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (is ++ [Sub s']), 1)]]     _ -> throwErrorWithTrace (TypeMismatch "symbol or integer" (Value fn))  addSuperscript :: String -> PrimitiveFunc addSuperscript = twoArgs $ \fn sub ->   case (fn, sub) of-    (ScalarData (SingleSymbol (Symbol id name is)), ScalarData s@(SingleSymbol (Symbol _ _ []))) ->-      return (ScalarData (SingleSymbol (Symbol id name (is ++ [Sup s]))))-    (ScalarData (SingleSymbol (Symbol id name is)), ScalarData s@(SingleTerm _ [])) ->-      return (ScalarData (SingleSymbol (Symbol id name (is ++ [Sup s]))))+    (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]]),+     CASData s@(CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol _ _ [], 1)]])) ->+      return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (is ++ [Sup s]), 1)]]+    (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]]),+     CASData s@(CASPoly [CASTerm (CASInteger _) []])) ->+      return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (is ++ [Sup s]), 1)]]+    (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name is, 1)]]),+     CASData s@(CASInteger _)) ->+      let s' = CASPoly [CASTerm s []]+      in return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol symId name (is ++ [Sup s']), 1)]]     _ -> throwErrorWithTrace (TypeMismatch "symbol" (Value fn))  updateFunctionArgs :: String -> PrimitiveFunc updateFunctionArgs = twoArgs' $ \funcVal newArgsColl ->   case (funcVal, newArgsColl) of-    (ScalarData (SingleTerm 1 [(FunctionData name _, 1)]), Collection argsSeq) -> do-      args' <- mapM extractScalar (toList argsSeq)-      return $ ScalarData (SingleTerm 1 [(FunctionData name args', 1)])+    (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData name _, 1)]]), Collection argsSeq) -> do+      args' <- mapM extractCAS (toList argsSeq)+      return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData name args', 1)]]     _ -> throwErrorWithTrace (TypeMismatch "function value and collection of scalars" (Value funcVal))  where-  extractScalar (ScalarData s) = return s-  extractScalar val = throwErrorWithTrace (TypeMismatch "scalar" (Value val))+  extractCAS (CASData cv) = return cv+  extractCAS val = throwErrorWithTrace (TypeMismatch "scalar" (Value val)) +-- | Build a function symbol from a computed name and argument values,+-- without going through a definition context (the `function (...)`+-- expression requires one).  Families of symbols can then be created+-- programmatically: map (\n -> functionSymbol (S.append "f" (show n)) [x]) ...+functionSymbol :: String -> PrimitiveFunc+functionSymbol = twoArgs' $ \nameVal argsColl ->+  case (nameVal, argsColl) of+    (String name, Collection argsSeq) -> do+      args' <- mapM extractCAS (toList argsSeq)+      let sym = CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol "" (T.unpack name) [], 1)]]+      return $ CASData $ CASPoly [CASTerm (CASInteger 1) [(CAS.FunctionData sym args', 1)]]+    _ -> throwErrorWithTrace (TypeMismatch "string and collection of scalars" (Value nameVal))+ where+  extractCAS (CASData cv) = return cv+  extractCAS val = throwErrorWithTrace (TypeMismatch "scalar" (Value val))++-- | Return the indices of an exact CAS symbol without erasing their kinds.+-- The mathValue `symbol` pattern exposes index payloads for historical+-- compatibility, which is insufficient for consumers that must distinguish+-- tensor Sub/Sup indices from FunctionData derivative User indices.  This+-- structural view uses the same public TensorIndex constructors as+-- tensorIndices and never relies on pretty-printed syntax.+symbolIndices :: String -> PrimitiveFunc+symbolIndices = oneArg' $ \value ->+  case value of+    CASData casValue ->+      case exactSymbol casValue of+        Just indices -> Collection . Sq.fromList <$> mapM publicIndex indices+        Nothing -> throwErrorWithTrace (TypeMismatch "symbol" (Value value))+    _ -> throwErrorWithTrace (TypeMismatch "symbol" (Value value))+ where+  exactSymbol+    (CAS.CASFactor (CAS.Symbol _ _ indices)) = Just indices+  exactSymbol+    (CAS.CASPoly+      [CAS.CASTerm (CAS.CASInteger 1) [(CAS.Symbol _ _ indices, 1)]]) =+        Just indices+  exactSymbol (CAS.CASFrac numerator (CAS.CASInteger 1)) =+    exactSymbol numerator+  exactSymbol _ = Nothing++  publicIndex (Sub index) =+    return $ InductiveData "SubIndex" [CASData index]+  publicIndex (Sup index) =+    return $ InductiveData "SupIndex" [CASData index]+  publicIndex (SupSub index) =+    return $ InductiveData "DiagIndex" [CASData index]+  publicIndex (User index) =+    return $ InductiveData "UserIndex" [CASData index]+  publicIndex (DF _ _) =+    throwErrorWithTrace (EgisonBug "symbolIndices encountered a DF index")+  publicIndex (MultiSub {}) =+    throwErrorWithTrace (EgisonBug "symbolIndices encountered an internal multi-subscript")+  publicIndex (MultiSup {}) =+    throwErrorWithTrace (EgisonBug "symbolIndices encountered an internal multi-superscript")++-- | Validate the CAS tree before analytic differentiation.  Reject an+-- application unless it is a FunctionData value, a registered unary+-- derivative, or the built-in general power operation.  Returning the input+-- unchanged lets the library reuse the existing differentiation engine after+-- validation without duplicating its product, quotient, and chain rules.+requireAnalyticDerivative :: String -> PrimitiveFunc+requireAnalyticDerivative = twoArgs' $ \valueVal variableVal ->+  case (valueVal, variableVal) of+    (CASData value, CASData variable) -> do+      derivativeRules <- Set.fromList <$> getDerivativeRuleNames+      case analyticDerivativeIssue derivativeRules value of+        Nothing -> return valueVal+        Just issue -> throwError $ Default $+          "analytic derivative: " ++ issue+          ++ " while differentiating with respect to " ++ CAS.prettyCAS variable+    (CASData _, other) ->+      throwErrorWithTrace (TypeMismatch "math expression" (Value other))+    (other, _) ->+      throwErrorWithTrace (TypeMismatch "math expression" (Value other))++analyticDerivativeIssue :: Set.Set String -> CAS.CASValue -> Maybe String+analyticDerivativeIssue derivativeRules = goValue+ where+  goValue (CAS.CASInteger _) = Nothing+  goValue (CAS.CASFactor symbol) = goSymbol symbol+  goValue (CAS.CASPoly terms) = firstIssue (map goTerm terms)+  goValue (CAS.CASFrac numerator denominator) =+    firstIssue [goValue numerator, goValue denominator]++  goTerm (CAS.CASTerm coefficient factors) =+    firstIssue (goValue coefficient : map (goSymbol . fst) factors)++  goSymbol (CAS.Symbol {}) = Nothing+  goSymbol (CAS.FunctionData _ arguments) =+    firstIssue (map goValue arguments)+  goSymbol (CAS.Apply1 function argument) =+    case analyticFunctionName function of+      Just name+        | name `Set.member` derivativeRules -> goValue argument+        | otherwise -> unsupportedApplication name 1+      Nothing -> unsupportedApplication (CAS.prettyCAS function) 1+  goSymbol (CAS.Apply2 function base exponent)+    | analyticFunctionName function == Just "^" =+        firstIssue [goValue base, goValue exponent]+    | otherwise =+        unsupportedApplication (analyticFunctionLabel function) 2+  goSymbol (CAS.Apply3 function _ _ _) =+    unsupportedApplication (analyticFunctionLabel function) 3+  goSymbol (CAS.Apply4 function _ _ _ _) =+    unsupportedApplication (analyticFunctionLabel function) 4+  goSymbol (CAS.Quote value) = goValue value+  goSymbol (CAS.QuoteFunction whnf) =+    unsupportedApplication+      (maybe "<anonymous function>" id (prettyFunctionName whnf)) 0++  unsupportedApplication :: String -> Int -> Maybe String+  unsupportedApplication name arity = Just $+    "no registered derivative rule for " ++ name+    ++ " with arity " ++ show arity++  analyticFunctionLabel function =+    maybe (CAS.prettyCAS function) id (analyticFunctionName function)++firstIssue :: [Maybe a] -> Maybe a+firstIssue [] = Nothing+firstIssue (Just issue : _) = Just issue+firstIssue (Nothing : rest) = firstIssue rest++analyticFunctionName :: CAS.CASValue -> Maybe String+analyticFunctionName+  (CAS.CASFactor (CAS.QuoteFunction whnf)) = prettyFunctionName whnf+analyticFunctionName+  (CAS.CASPoly+    [CAS.CASTerm (CAS.CASInteger 1) [(CAS.QuoteFunction whnf, 1)]]) =+      prettyFunctionName whnf+analyticFunctionName+  (CAS.CASFactor (CAS.Symbol _ name _)) = Just name+analyticFunctionName+  (CAS.CASPoly+    [CAS.CASTerm (CAS.CASInteger 1) [(CAS.Symbol _ name _, 1)]]) = Just name+analyticFunctionName _ = Nothing++-- | Re-wrap a scalar in a quote atom (the backtick of the surface+-- syntax, as a function).  Needed by mapSymbols to rebuild a quoted+-- factor after substituting inside it.+quoteScalar :: String -> PrimitiveFunc+quoteScalar = oneArg' $ \v -> case v of+  CASData cv -> return $ quoteCASData cv+  _          -> throwErrorWithTrace (TypeMismatch "scalar" (Value v))++-- | Name of a math function value (the head bound by the apply1..4+-- patterns of the mathValue matcher), e.g. cos for (cos x).  Replaces+-- show-and-split hacks in code generators.+mathFunctionName :: String -> PrimitiveFunc+mathFunctionName = oneArg $ \val -> case val of+  Func (Just (Var name _)) _ _ _ -> return $ String (T.pack name)+  _ -> throwErrorWithTrace (TypeMismatch "named function" (Value val))++-- | Convert a CASValue into a list of single-term polynomials.+-- A CASPoly is decomposed into its terms; an integer/factor becomes a singleton list;+-- zero becomes the empty list. Used by the parametric `poly` matcher to expose+-- term-level decomposition to Egison code.+casTermsPrim :: String -> PrimitiveFunc+casTermsPrim = oneArg' $ \v -> case v of+  CASData cv -> return . Collection . Sq.fromList . map CASData $ casValueToTerms cv+  _ -> throwErrorWithTrace (TypeMismatch "CAS value" (Value v))++casValueToTerms :: CAS.CASValue -> [CAS.CASValue]+casValueToTerms (CAS.CASInteger 0) = []+casValueToTerms v@(CAS.CASInteger _) = [CAS.CASPoly [CASTerm v []]]+casValueToTerms (CAS.CASFactor sym) = [CAS.CASPoly [CASTerm (CAS.CASInteger 1) [(sym, 1)]]]+casValueToTerms (CAS.CASPoly ts) = map (\t -> CAS.CASPoly [t]) ts+casValueToTerms v@(CAS.CASFrac _ _) = [v]++-- | Build a CASValue from a collection of single-term polynomials.+-- Each element should be a single-term polynomial (as produced by casTerms);+-- the result is normalized by re-running through casPlus.+casFromTermsPrim :: String -> PrimitiveFunc+casFromTermsPrim = oneArg' $ \v -> case v of+  Collection seq_ -> do+    cvs <- mapM extractCAS (toList seq_)+    return . CASData $ foldr CAS.casPlus (CAS.CASInteger 0) cvs+  _ -> throwErrorWithTrace (TypeMismatch "collection of CAS values" (Value v))+ where+  extractCAS (CASData cv) = return cv+  extractCAS val = throwErrorWithTrace (TypeMismatch "CAS value" (Value val))++-- | Extract the coefficient of a single-term CASValue.+-- For a single-term polynomial CASPoly [CASTerm c _], returns c.+-- For a bare integer, returns it as-is. For a bare factor, returns 1.+termCoeffPrim :: String -> PrimitiveFunc+termCoeffPrim = oneArg' $ \v -> case v of+  CASData (CAS.CASPoly [CASTerm c _]) -> return $ CASData c+  CASData (CAS.CASPoly []) -> return $ CASData (CAS.CASInteger 0)+  CASData v0@(CAS.CASInteger _) -> return $ CASData v0+  CASData (CAS.CASFactor _) -> return $ CASData (CAS.CASInteger 1)+  CASData v0@(CAS.CASFrac _ _) -> return $ CASData v0+  _ -> throwErrorWithTrace (TypeMismatch "single-term CAS value" (Value v))++-- | Phase B: term-level rule application primitive.+--+-- Given a `term`-level rule with LHS `<lhsValue>` (typically a single-monomial+-- value like `i^2`, with implicit coefficient 1) and RHS `<rhsValue>`, apply+-- the rule recursively to each term inside `<input>`. For each term:+--   - If the term's monomial equals LHS's monomial, replace the term with+--     `coeff × rhsValue` (where coeff is the term's coefficient).+--   - Otherwise, keep the term as-is.+-- The result is the sum of all (possibly-transformed) terms.+--+-- This makes user `declare rule auto term LHS = RHS` declarations recurse+-- into polynomial terms, so e.g. `(1+i)*(1-i) = 1 - i^2` simplifies to `2`+-- when the rule `i^2 = -1` is registered, even without a built-in+-- `casRewriteI` rule.+applyTermRulePrim :: String -> PrimitiveFunc+applyTermRulePrim = threeArgs' $ \lhsV rhsV inputV ->+  case (lhsV, rhsV, inputV) of+    (CASData lhs, CASData rhs, CASData input) ->+      return $ CASData $ applyTermRuleCAS lhs rhs input+    _ -> throwErrorWithTrace (TypeMismatch "CAS values" (Value lhsV))++-- | The actual term-level rule application logic at the CASValue level.+--+-- Containment matching: the LHS monomial is treated as a *factor* of the+-- target term's monomial. If the target contains LHS as a sub-factor, we+-- replace one occurrence of LHS with RHS. With `iterateRules` calling this+-- repeatedly to fixpoint, higher powers like `u^4` reduce as `u^4 → -u^2 →+-- 1` for the rule `u^2 = -1`.+applyTermRuleCAS :: CAS.CASValue -> CAS.CASValue -> CAS.CASValue -> CAS.CASValue+applyTermRuleCAS lhsValue rhsValue input =+  case extractLhsMonomial lhsValue of+    Nothing -> input  -- Can't extract a monomial from LHS → can't apply+    Just lhsMono -> transformValue lhsMono rhsValue input+  where+    -- Extract the monomial part of a single-coefficient-1 term value.+    extractLhsMonomial :: CAS.CASValue -> Maybe CAS.Monomial+    extractLhsMonomial (CAS.CASPoly [CAS.CASTerm (CAS.CASInteger 1) mono]) = Just mono+    extractLhsMonomial (CAS.CASFactor sym) = Just [(sym, 1)]+    extractLhsMonomial _ = Nothing++    -- Apply to a value, recursing into Frac numerator/denominator.+    transformValue :: CAS.Monomial -> CAS.CASValue -> CAS.CASValue -> CAS.CASValue+    transformValue lhsMono rhs (CAS.CASPoly terms) =+      foldr CAS.casPlus (CAS.CASInteger 0)+            (map (transformTerm lhsMono rhs) terms)+    transformValue lhsMono rhs (CAS.CASFrac n d) =+      CAS.casFrac (transformValue lhsMono rhs n) d+    transformValue _ _ v = v++    -- Apply to a single term: if the term's monomial contains LHS as a+    -- factor, replace that factor with RHS. Otherwise keep the term.+    transformTerm :: CAS.Monomial -> CAS.CASValue -> CAS.CASTerm -> CAS.CASValue+    transformTerm [] _rhs (CAS.CASTerm coeff mono) =+      -- LHS is the empty monomial (constant 1). Don't transform terms here+      -- (avoid replacing every term with rhs); the user should use a+      -- poly-level rule for constant replacement.+      CAS.CASPoly [CAS.CASTerm coeff mono]+    transformTerm lhsMono rhs (CAS.CASTerm coeff mono) =+      case monomialContains lhsMono mono of+        Just remaining ->+          -- coeff × rhs × (remaining monomial)+          let remTerm = CAS.CASPoly [CAS.CASTerm coeff remaining]+          in CAS.casMult remTerm rhs+        Nothing ->+          CAS.CASPoly [CAS.CASTerm coeff mono]++    -- Check if `lhsMono` is contained in `target` as a sub-factor. If so,+    -- return the remaining monomial (target minus lhsMono). Otherwise Nothing.+    -- Each (sym, exp) in lhsMono must be matched by (sym, exp') in target+    -- with exp' >= exp.+    monomialContains :: CAS.Monomial -> CAS.Monomial -> Maybe CAS.Monomial+    monomialContains [] target = Just target+    monomialContains ((sym, expL) : rest) target =+      case lookup sym target of+        Just expT | expT >= expL ->+          -- Subtract: keep all entries except (sym, _), then add (sym, expT-expL)+          -- if the remaining exponent is positive+          let target' = [(s, e) | (s, e) <- target, s /= sym]+                     ++ [(sym, expT - expL) | expT > expL]+          in monomialContains rest target'+        _ -> Nothing++-- | Phase A.5: structural traversal primitives `mapPoly`, `mapTerm`, `mapFrac`.+-- Each takes a user closure `f : MathValue -> MathValue` and a CAS value, and+-- recursively applies `f` at the corresponding granularity, descending into+-- sub-MathValues inside Apply1-4 / Quote / Function / Symbol indices.+--+-- - mapFrac f v : applies f at every Frac node (every MathValue is a Frac)+-- - mapPoly f v : applies f at every (sub-)polynomial; same nodes as mapFrac+--                 since a MathValue with denom=1 is also a poly. Useful when+--                 the user's pattern targets a poly shape (e.g. `$a + $b`).+-- - mapTerm f v : applies f to each *term* of every poly, lifting each term+--                 to a single-term MathValue before passing to f.+--+-- All variants iterate `f` at each visited node until a fixpoint (no change).+-- Traversal is bottom-up: deeper sub-expressions are reduced first.+mapPolyPrim :: String -> PrimitiveFunc+mapPolyPrim _ args = case args of+  [funcVal, CASData v] -> CASData <$> mapPolyCAS funcVal v+  (a:_) -> throwErrorWithTrace (TypeMismatch "function and CAS value" (Value a))+  []    -> throwError $ Default "mapPolyAll: no arguments"++mapTermPrim :: String -> PrimitiveFunc+mapTermPrim _ args = case args of+  [funcVal, CASData v] -> CASData <$> mapTermCAS funcVal v+  (a:_) -> throwErrorWithTrace (TypeMismatch "function and CAS value" (Value a))+  []    -> throwError $ Default "mapTermAll: no arguments"++mapFracPrim :: String -> PrimitiveFunc+mapFracPrim _ args = case args of+  [funcVal, CASData v] -> CASData <$> mapFracCAS funcVal v+  (a:_) -> throwErrorWithTrace (TypeMismatch "function and CAS value" (Value a))+  []    -> throwError $ Default "mapFracAll: no arguments"++-- | Apply a user closure to a CAS value, returning the resulting CAS value.+applyRuleClosure :: EgisonValue -> CAS.CASValue -> EvalM CAS.CASValue+applyRuleClosure f cv = do+  ref <- newEvaluatedObjectRef (Value (CASData cv))+  result <- applyRef nullEnv (Value f) [ref]+  case result of+    Value (CASData cv') -> return cv'+    other -> throwError $ Default $ "rule must return a CAS value, but got: " ++ show other++-- | Apply rule until fixpoint (CAS values become equal across iterations).+applyRuleFix :: EgisonValue -> CAS.CASValue -> EvalM CAS.CASValue+applyRuleFix f cv = do+  cv' <- applyRuleClosure f cv+  if cv' == cv then return cv else applyRuleFix f cv'++-- | CAS-specialised replacement for the lib's `iterateRules`. Runs the+-- entire rule-application + fixpoint loop in Haskell, eliminating the+-- per-iteration Egison-level fold/recursion/== overhead. Emitted by+-- desugar for `mathNormalize` (see Desugar.hs `buildMathNormalizeRedef`).+iterateRulesCASPrim :: String -> PrimitiveFunc+iterateRulesCASPrim name args = case args of+  -- Triggers come from EvalState (cached as [Set String] at desugar time);+  -- no per-call Set construction. The rules list and value are passed in.+  [Collection ruleSeq, CASData v0] -> do+    triggerSets <- getAutoRuleTriggers+    let rules = toList ruleSeq+        pairs = zip triggerSets rules+    CASData <$> iterateRulesLoopWithTriggers pairs v0+  [_, v] -> return v+  _ -> throwErrorWithTrace (ArgumentsNumPrimitive name 2 (length args))++-- | Per-term trigger guard for the generated pattern-rule steps+-- (Desugar.buildPatternRuleBody): does the sub-value contain any of the+-- given names?  Short-circuits on the first hit.  Sound as a guard with+-- ANY-of semantics: a sub-value the rule's LHS can match necessarily+-- contains the pattern's head symbol, which is in the trigger set+-- (extra names in the set only let more sub-values through).+casContainsAnySymbolPrim :: String -> PrimitiveFunc+casContainsAnySymbolPrim name args = case args of+  [Collection nameSeq, CASData v] -> do+    names <- mapM fromStringValue (toList nameSeq)+    return (Bool (casContainsAny (Set.fromList names) v))+  [_, _] -> return (Bool True)  -- fail open: never block a rule+  _ -> throwErrorWithTrace (ArgumentsNumPrimitive name 2 (length args))+ where+  fromStringValue (String t) = return (T.unpack t)+  fromStringValue v          = throwErrorWithTrace (TypeMismatch "string" (Value v))++casContainsAny :: Set.Set String -> CAS.CASValue -> Bool+casContainsAny names =+  getAny . foldCASSymbolNames (Any . (`Set.member` names))++-- | Walks the CASValue once and collects the set of every Symbol /+-- QuoteFunction / FunctionData name that appears anywhere in the tree.+-- Used by iterateRulesCAS to skip rules whose triggers can't possibly fire.+casCollectSymbols :: CAS.CASValue -> Set.Set String+casCollectSymbols = foldCASSymbolNames Set.singleton++-- | The one traversal behind the two symbol scans above: fold every+-- name-bearing leaf of a CASValue (Symbol names, printable+-- QuoteFunction names, and everything reachable through Apply/Quote/+-- FunctionData sub-values) into a monoid.  With Any the (||) fold+-- short-circuits on the first hit, so the membership test keeps its+-- early exit; with Set it collects.+foldCASSymbolNames :: Monoid m => (String -> m) -> CAS.CASValue -> m+foldCASSymbolNames leaf = goV+ where+  goV (CAS.CASInteger _)  = mempty+  goV (CAS.CASFactor sym) = goSym sym+  goV (CAS.CASPoly terms) = foldMap goTerm terms+  goV (CAS.CASFrac n d)   = goV n <> goV d+  goTerm (CAS.CASTerm coeff mono) = goV coeff <> foldMap (goSym . fst) mono+  goSym (CAS.Symbol _ nm _)        = leaf nm+  goSym (CAS.Apply1 f a1)          = goV f <> goV a1+  goSym (CAS.Apply2 f a1 a2)       = goV f <> goV a1 <> goV a2+  goSym (CAS.Apply3 f a1 a2 a3)    = goV f <> goV a1 <> goV a2 <> goV a3+  goSym (CAS.Apply4 f a1 a2 a3 a4) = goV f <> goV a1 <> goV a2 <> goV a3 <> goV a4+  goSym (CAS.Quote v)              = goV v+  goSym (CAS.QuoteFunction whnf)   = maybe mempty leaf (prettyFunctionName whnf)+  goSym (CAS.FunctionData fn as)   = goV fn <> foldMap goV as++iterateRulesLoopWithTriggers+  :: [(Set.Set String, EgisonValue)] -> CAS.CASValue -> EvalM CAS.CASValue+iterateRulesLoopWithTriggers pairs v0 =+  -- Fast path: a pure CASInteger has no symbols, so only rules with an+  -- empty trigger set (e.g. `$x^3 = 0`) can possibly fire on it. Filter+  -- once; if none remain, return immediately without entering the loop.+  let activePairs = case v0 of+        CAS.CASInteger _ -> filter (Set.null . fst) pairs+        _                -> pairs+  in if null activePairs+       then return v0+       else loop activePairs v0+ where+  -- One CAS scan per iteration (not per rule). Rules that introduce+  -- symbols not present at iteration start will still fire in the next+  -- iteration via the outer fixpoint check, so correctness is preserved+  -- and we save N-1 deep-compare + scan calls per iteration.+  loop ps v = do+    let presentSyms = casCollectSymbols v+    v' <- foldM (step presentSyms) v ps+    if v' == v then return v else loop ps v'+  step presentSyms acc (triggers, rule)+    | Set.null triggers           = applyRuleClosure rule acc+    | Set.disjoint triggers presentSyms = return acc+    | otherwise                   = applyRuleClosure rule acc+++-- | mapPoly / mapFrac: traverse, apply at each (sub-)MathValue node.+-- Sub-recursion is gated on the outer "structure" of the value: we recurse+-- into Apply1-4/Quote/FunctionData arguments (which are user-visible+-- MathValue sub-expressions) and into Frac numerator/denominator. We do NOT+-- recurse into a term's *coefficient* slot, because that's an internal+-- representation detail (e.g. `2*x` has coef 2 sitting next to the monomial+-- `x`; the user's rule would otherwise be applied to bare integer 2).+mapFracCAS :: EgisonValue -> CAS.CASValue -> EvalM CAS.CASValue+mapFracCAS = mapPolyCAS++mapPolyCAS :: EgisonValue -> CAS.CASValue -> EvalM CAS.CASValue+mapPolyCAS f v = do+  v' <- descendCASNoCoef (mapPolyCAS f) v+  applyRuleFix f v'++-- | mapTerm: traverse, apply at each Term (lifted to single-term MathValue).+mapTermCAS :: EgisonValue -> CAS.CASValue -> EvalM CAS.CASValue+mapTermCAS f v = do+  v' <- descendCASNoCoef (mapTermCAS f) v+  case v' of+    CAS.CASPoly terms -> do+      newTerms <- mapM (\t -> applyRuleFix f (CAS.CASPoly [t])) terms+      return $ foldr CAS.casPlus (CAS.CASInteger 0) newTerms+    _ -> applyRuleFix f v'++-- | Variant of descendCAS that does NOT recurse into term coefficients.+-- Used by mapPoly/mapTerm/mapFrac: the coefficient is a raw integer scalar+-- representing multiplicity, not a user-rewriteable sub-expression.+descendCASNoCoef :: (CAS.CASValue -> EvalM CAS.CASValue) -> CAS.CASValue -> EvalM CAS.CASValue+descendCASNoCoef _ v@(CAS.CASInteger _) = return v+descendCASNoCoef recur (CAS.CASFactor sym) = CAS.casNormalize . CAS.CASFactor <$> descendSymbol recur sym+descendCASNoCoef recur (CAS.CASPoly terms) = (CAS.casNormalize . CAS.CASPoly) <$> mapM (descendTermNoCoef recur) terms+descendCASNoCoef recur (CAS.CASFrac n d) = CAS.casFrac <$> recur n <*> recur d++descendTermNoCoef :: (CAS.CASValue -> EvalM CAS.CASValue) -> CAS.CASTerm -> EvalM CAS.CASTerm+descendTermNoCoef recur (CAS.CASTerm coef factors) = do+  factors' <- mapM (\(sym, e) -> (\s -> (s, e)) <$> descendSymbol recur sym) factors+  return $ CAS.CASTerm coef factors'++-- | Recurse into sub-CASValues inside the structure of a CASValue.+-- Calls `recur` on each immediate sub-MathValue (in Apply args, Quote, Function,+-- and the coefficient slot of each term). Does NOT call f on the value itself.+-- The reconstructed value is re-normalized via casNormalize so that downstream+-- pattern matching sees canonical form (the matcher relies on Frac (Plus ...)+-- shape, which raw `CASPoly` constructors may not satisfy after edits).+descendSymbol :: (CAS.CASValue -> EvalM CAS.CASValue) -> CAS.SymbolExpr -> EvalM CAS.SymbolExpr+descendSymbol recur (CAS.Symbol sid name idxs) = do+  idxs' <- mapM (traverse recur) idxs+  return $ CAS.Symbol sid name idxs'+-- Apply1-4: recurse only on the *arguments*; the function slot holds a+-- symbolic reference that is not itself a rewrite target.+descendSymbol recur (CAS.Apply1 fn a) = CAS.Apply1 fn <$> recur a+descendSymbol recur (CAS.Apply2 fn a b) = CAS.Apply2 fn <$> recur a <*> recur b+descendSymbol recur (CAS.Apply3 fn a b c) = CAS.Apply3 fn <$> recur a <*> recur b <*> recur c+descendSymbol recur (CAS.Apply4 fn a b c d) = CAS.Apply4 fn <$> recur a <*> recur b <*> recur c <*> recur d+descendSymbol recur (CAS.Quote v) = CAS.Quote <$> recur v+-- FunctionData: name is the symbolic function reference, args are MathValue args.+descendSymbol recur (CAS.FunctionData name args) = CAS.FunctionData name <$> mapM recur args+descendSymbol _ s@(CAS.QuoteFunction _) = return s++-- | Phase 7.4/7.5: report the number of `declare rule` declarations seen by+-- the env-builder. The rule data itself is held in EnvBuildResult; here we+-- only expose the count so users can confirm registration worked.+numReductionRulesPrim :: String -> PrimitiveFunc+numReductionRulesPrim _ args = case args of+  [] -> do+    n <- getReductionRulesCount+    return $ toEgison (fromIntegral n :: Integer)+  [Tuple []] -> do+    n <- getReductionRulesCount+    return $ toEgison (fromIntegral n :: Integer)+  (a:_)  -> throwErrorWithTrace (TypeMismatch "no arguments" (Value a))++-- | Phase 6.3: report the number of `declare derivative` declarations seen.+numDerivativeRulesPrim :: String -> PrimitiveFunc+numDerivativeRulesPrim _ args = case args of+  [] -> do+    n <- getDerivativeRulesCount+    return $ toEgison (fromIntegral n :: Integer)+  [Tuple []] -> do+    n <- getDerivativeRulesCount+    return $ toEgison (fromIntegral n :: Integer)+  (a:_)  -> throwErrorWithTrace (TypeMismatch "no arguments" (Value a))++-- | List the names of all named reduction rules (auto rules excluded).+ruleNamesPrim :: String -> PrimitiveFunc+ruleNamesPrim _ args = case args of+  [] -> do+    ns <- getReductionRuleNames+    return $ Collection $ Sq.fromList $ map (String . T.pack) ns+  [Tuple []] -> do+    ns <- getReductionRuleNames+    return $ Collection $ Sq.fromList $ map (String . T.pack) ns+  (a:_)  -> throwErrorWithTrace (TypeMismatch "no arguments" (Value a))++-- | List the function names that have a registered derivative.+derivativeNamesPrim :: String -> PrimitiveFunc+derivativeNamesPrim _ args = case args of+  [] -> do+    ns <- getDerivativeRuleNames+    return $ Collection $ Sq.fromList $ map (String . T.pack) ns+  [Tuple []] -> do+    ns <- getDerivativeRuleNames+    return $ Collection $ Sq.fromList $ map (String . T.pack) ns+  (a:_)  -> throwErrorWithTrace (TypeMismatch "no arguments" (Value a))++-- | Check whether a named reduction rule is registered.+hasReductionRulePrim :: String -> PrimitiveFunc+hasReductionRulePrim = oneArg' $ \v -> case v of+  String s -> do+    ns <- getReductionRuleNames+    return $ Bool (T.unpack s `elem` ns)+  _ -> throwErrorWithTrace (TypeMismatch "string rule name" (Value v))++-- | Check whether a function name has a registered derivative.+hasDerivativeRulePrim :: String -> PrimitiveFunc+hasDerivativeRulePrim = oneArg' $ \v -> case v of+  String s -> do+    ns <- getDerivativeRuleNames+    return $ Bool (T.unpack s `elem` ns)+  _ -> throwErrorWithTrace (TypeMismatch "string function name" (Value v))++-- | True if a CASValue references any of the named symbols or functions+-- anywhere in its tree. Used by `declare rule auto` desugaring as a fast+-- pre-filter so rules whose trigger symbols are absent from the value can+-- skip the per-arithmetic match attempt.+containsAnySymbolPrim :: String -> PrimitiveFunc+containsAnySymbolPrim = twoArgs' $ \namesV valV ->+  case (namesV, valV) of+    (Collection nameSeq, CASData cv) -> do+      names <- mapM extractName (toList nameSeq)+      return $ Bool (casContainsAnyName names cv)+    _ -> throwErrorWithTrace (TypeMismatch "string list and CAS value" (Value valV))+ where+  extractName (String s) = return (T.unpack s)+  extractName v          = throwErrorWithTrace (TypeMismatch "string symbol name" (Value v))++casContainsAnyName :: [String] -> CAS.CASValue -> Bool+casContainsAnyName names = goV+ where+  goV (CAS.CASInteger _)  = False+  goV (CAS.CASFactor sym) = goSym sym+  goV (CAS.CASPoly terms) = any goTerm terms+  goV (CAS.CASFrac n d)   = goV n || goV d+  goTerm (CAS.CASTerm coeff mono) = goV coeff || any goFactor mono+  goFactor (sym, _)               = goSym sym+  goSym (CAS.Symbol _ name _)      = name `elem` names+  goSym (CAS.Apply1 f a1)          = goV f || goV a1+  goSym (CAS.Apply2 f a1 a2)       = goV f || goV a1 || goV a2+  goSym (CAS.Apply3 f a1 a2 a3)    = goV f || goV a1 || goV a2 || goV a3+  goSym (CAS.Apply4 f a1 a2 a3 a4) = goV f || goV a1 || goV a2 || goV a3 || goV a4+  goSym (CAS.Quote v)              = goV v+  goSym (CAS.QuoteFunction whnf)   = case prettyFunctionName whnf of+                                       Just n  -> n `elem` names+                                       Nothing -> False+  goSym (CAS.FunctionData fn args) = goV fn || any goV args++-- | Phase 5.5: runtime check that all atoms in a CAS value belong to the+-- given allowed-atom-name list. Used by user-level coerce-style helpers.+-- Atom names are pretty-printed (e.g. "x", "sqrt 2"), matched as strings.+isInPolyAtomsPrim :: String -> PrimitiveFunc+isInPolyAtomsPrim = twoArgs $ \v allowedC ->+  case (v, allowedC) of+    (CASData cv, Collection allowedSeq) -> do+      allowedNames <- mapM extractName (toList allowedSeq)+      let valueAtoms = CAS.casAtomSet cv+      return $ Bool (all (`elem` allowedNames) valueAtoms)+    _ -> throwErrorWithTrace (TypeMismatch "CAS value and string list" (Value v))+ where+  extractName (String s) = return (T.unpack s)+  extractName _          = throwErrorWithTrace (TypeMismatch "string atom name" (Value v))+  v = CASData (CAS.CASInteger 0)  -- unused placeholder for the error path++-- | Phase 5.5: check if a CASValue is a pure integer (no atoms / fractions).+isPureIntegerPrim :: String -> PrimitiveFunc+isPureIntegerPrim = oneArg' $ \v -> case v of+  CASData (CAS.CASInteger _)  -> return $ Bool True+  CASData (CAS.CASPoly [])    -> return $ Bool True   -- canonical zero+  CASData (CAS.CASPoly [CAS.CASTerm (CAS.CASInteger _) []]) -> return $ Bool True+  CASData _                   -> return $ Bool False+  _                           -> return $ Bool False++-- | Phase 5.5: check if a CASValue is a pure rational (Frac of integers).+isPureFractionPrim :: String -> PrimitiveFunc+isPureFractionPrim = oneArg' $ \v -> case v of+  CASData (CAS.CASInteger _) -> return $ Bool True+  CASData (CAS.CASFrac (CAS.CASInteger _) (CAS.CASInteger _)) -> return $ Bool True+  CASData (CAS.CASPoly []) -> return $ Bool True+  CASData (CAS.CASPoly [CAS.CASTerm (CAS.CASInteger _) []]) -> return $ Bool True+  _ -> return $ Bool False++-- | Phase 8: differential closure check.+-- Returns True iff the output value's atom set is a subset of the input's,+-- i.e. ∂/∂ did not introduce any new atoms. Used to label results that+-- stayed in the same Poly Integer [atoms] sub-ring.+differentialClosedPrim :: String -> PrimitiveFunc+differentialClosedPrim = twoArgs $ \input output ->+  case (input, output) of+    (CASData iv, CASData ov) ->+      return $ Bool (CAS.casDifferentialClosed iv ov)+    _ -> throwErrorWithTrace (TypeMismatch "two CAS values" (Value input))++-- | Phase 8: print the value alongside its observed type for REPL inspection.+-- Returns a string of the form "value : observed-type".+inspectPrim :: String -> PrimitiveFunc+inspectPrim = oneArg' $ \v -> case v of+  CASData cv ->+    return $ String (T.pack (CAS.prettyCAS cv ++ " : " ++ CAS.prettyTypeOf cv))+  Bool b ->+    return $ String (T.pack (show b ++ " : Bool"))+  Char c ->+    return $ String (T.pack (show c ++ " : Char"))+  String s ->+    return $ String (T.pack (show s ++ " : String"))+  Float f ->+    return $ String (T.pack (show f ++ " : Float"))+  Tuple [] ->+    return $ String (T.pack "() : ()")+  Tuple xs -> do+    descs <- mapM describeOne xs+    let valStr = "(" ++ intercalateComma (map fst descs) ++ ")"+        tyStr  = "(" ++ intercalateComma (map snd descs) ++ ")"+    return $ String (T.pack (valStr ++ " : " ++ tyStr))+  _ -> return $ String (T.pack "<value> : Any")+ where+  describeOne (CASData cv) = return (CAS.prettyCAS cv, CAS.prettyTypeOf cv)+  describeOne (Bool b)     = return (show b, "Bool")+  describeOne (Char c)     = return (show c, "Char")+  describeOne (String s)   = return (show s, "String")+  describeOne (Float f)    = return (show f, "Float")+  describeOne _            = return ("<value>", "Any")+  intercalateComma []     = ""+  intercalateComma [s]    = s+  intercalateComma (s:ss) = s ++ ", " ++ intercalateComma ss++-- | Phase 8 observed type: report the most specific runtime type of a value+-- as a string.+-- | Identity at runtime; typed `forall a b. a -> b` (Type/Check.hs).+-- The unsafe static cast used ONLY by the code generated from+-- `declare cas-quotient` (projQ / reprQ between a quotient's nominal type+-- and its base representation). Not intended for user code.+casQuotientCastPrim :: String -> PrimitiveFunc+casQuotientCastPrim = oneArg' return++typeOfPrim :: String -> PrimitiveFunc+typeOfPrim = oneArg' $ \v -> case v of+  CASData cv -> return $ String (T.pack (CAS.prettyTypeOf cv))+  Tuple [] -> return $ String (T.pack "()")+  Tuple xs -> return $ String (T.pack ("(" ++ intercalateComma (map describeValue xs) ++ ")"))+  Collection _ -> return $ String (T.pack "Collection")+  TensorData _ -> return $ String (T.pack "Tensor")+  Bool _ -> return $ String (T.pack "Bool")+  Char _ -> return $ String (T.pack "Char")+  String _ -> return $ String (T.pack "String")+  Float _ -> return $ String (T.pack "Float")+  _ -> return $ String (T.pack "Any")+ where+  describeValue (CASData cv) = CAS.prettyTypeOf cv+  describeValue (Bool _)     = "Bool"+  describeValue (Char _)     = "Char"+  describeValue (String _)   = "String"+  describeValue (Float _)    = "Float"+  describeValue _            = "Any"+  intercalateComma []     = ""+  intercalateComma [s]    = s+  intercalateComma (s:ss) = s ++ ", " ++ intercalateComma ss++-- (`runtimeType` is intentionally NOT exposed as a user primitive: the+-- shallow runtime type is computed inside Eval.hs for type-class dispatch+-- only, so MathValue subtype types do not become first-class Egison values.)++-- | Extract the monomial of a single-term CASValue as a flat list of (factor, exponent) pairs.+-- For CASPoly [CASTerm _ mono], returns mono as a Collection of Tuple [factor, integer].+termMonomialPrim :: String -> PrimitiveFunc+termMonomialPrim = oneArg' $ \v -> case v of+  CASData (CAS.CASPoly [CASTerm _ mono]) -> return $ monoToCollection mono+  CASData (CAS.CASPoly []) -> return $ Collection Sq.empty+  CASData (CAS.CASInteger _) -> return $ Collection Sq.empty+  CASData (CAS.CASFactor sym) -> return $ monoToCollection [(sym, 1)]+  CASData (CAS.CASFrac _ _) -> return $ Collection Sq.empty+  _ -> throwErrorWithTrace (TypeMismatch "single-term CAS value" (Value v))+ where+  monoToCollection :: CAS.Monomial -> EgisonValue+  monoToCollection mono =+    Collection . Sq.fromList $+      map (\(sym, e) ->+            Tuple [ CASData (CAS.CASFactor sym)+                  , CASData (CAS.CASInteger e)+                  ]) mono+ assert ::  String -> PrimitiveFunc assert = twoArgs' $ \label test -> do   test <- fromEgison test@@ -184,8 +969,9 @@   extractIntList x = (:[]) <$> extractInt x      extractInt :: EgisonValue -> EvalM Integer-  extractInt (ScalarData s) = fromEgison (ScalarData s)-  extractInt val = throwErrorWithTrace (TypeMismatch "integer" (Value val))+  extractInt val = case val of+    CASData _ -> fromEgison val+    _         -> throwErrorWithTrace (TypeMismatch "integer" (Value val))      -- Sort lists lexicographically and calculate permutation sign using bubble sort   sortWithPermSign :: [[Integer]] -> (Integer, [[Integer]])
hs-src/Language/Egison/Primitives/Arith.hs view
@@ -12,6 +12,8 @@   ( primitiveArithFunctions   ) where +import           Data.Ratio                       ((%))+ import           Language.Egison.Data import           Language.Egison.Math import           Language.Egison.Primitives.Utils@@ -32,8 +34,6 @@   , ("f./", floatBinaryOp (/))   , ("numerator",       numerator')   , ("denominator",     denominator')-  , ("fromMathExpr",    fromScalarData)-  , ("toMathExpr'",     toScalarData)   , ("symbolNormalize", symbolNormalize)    , ("i.modulo",   integerBinaryOp mod)@@ -98,52 +98,44 @@ -- -- Arith ---scalarBinaryOp :: (ScalarData -> ScalarData -> ScalarData) -> String -> PrimitiveFunc-scalarBinaryOp mOp = twoArgs scalarBinaryOp'++-- | Binary operation on CASValue+casBinaryOp :: (CASValue -> CASValue -> CASValue) -> String -> PrimitiveFunc+casBinaryOp op = twoArgs casBinaryOp'  where-  scalarBinaryOp' (ScalarData m1) (ScalarData m2) = (return . ScalarData) (mOp m1 m2)-  scalarBinaryOp' (ScalarData _)  val             = throwErrorWithTrace (TypeMismatch "number" (Value val))-  scalarBinaryOp' val             _               = throwErrorWithTrace (TypeMismatch "number" (Value val))+  casBinaryOp' (CASData c1) (CASData c2) = return $ CASData (op c1 c2)+  casBinaryOp' (CASData _)  val          = throwErrorWithTrace (TypeMismatch "number" (Value val))+  casBinaryOp' val          _            = throwErrorWithTrace (TypeMismatch "number" (Value val))  plus :: String -> PrimitiveFunc-plus = scalarBinaryOp mathPlus+plus = casBinaryOp casPlus  minus :: String -> PrimitiveFunc-minus = scalarBinaryOp (\m1 m2 -> mathPlus m1 (mathNegate m2))+minus = casBinaryOp casMinus  multiply :: String -> PrimitiveFunc-multiply = scalarBinaryOp mathMult+multiply = casBinaryOp casMult  divide :: String -> PrimitiveFunc-divide = scalarBinaryOp mathDiv+divide = casBinaryOp casDivide  numerator' :: String -> PrimitiveFunc numerator' = oneArg numerator''  where-  numerator'' (ScalarData m) = return $ ScalarData (mathNumerator m)-  numerator'' val            = throwErrorWithTrace (TypeMismatch "rational" (Value val))+  numerator'' (CASData c) = return $ CASData (casNumerator c)+  numerator'' val         = throwErrorWithTrace (TypeMismatch "rational" (Value val))  denominator' :: String -> PrimitiveFunc denominator' = oneArg denominator''  where-  denominator'' (ScalarData m) = return $ ScalarData (mathDenominator m)-  denominator'' val            = throwErrorWithTrace (TypeMismatch "rational" (Value val))--fromScalarData :: String -> PrimitiveFunc-fromScalarData = oneArg fromScalarData'- where-  fromScalarData' (ScalarData m) = return $ mathExprToEgison m-  fromScalarData' val            = throwErrorWithTrace (TypeMismatch "number" (Value val))--toScalarData :: String -> PrimitiveFunc-toScalarData = oneArg $ \val ->-  ScalarData . mathNormalize' <$> egisonToScalarData val+  denominator'' (CASData c) = return $ CASData (casDenominator c)+  denominator'' val         = throwErrorWithTrace (TypeMismatch "rational" (Value val))  symbolNormalize :: String -> PrimitiveFunc symbolNormalize = oneArg $ \val ->   case val of-    ScalarData s -> return $ ScalarData (rewriteSymbol s)-    _            -> throwErrorWithTrace (TypeMismatch "math expression" (Value val))+    CASData c -> return $ CASData (casRewriteSymbol c)+    _         -> throwErrorWithTrace (TypeMismatch "math expression" (Value val))  -- -- Pred@@ -155,12 +147,13 @@ integerCompare :: (forall a. Ord a => a -> a -> Bool) -> String -> PrimitiveFunc integerCompare cmp = twoArgs' $ \val1 val2 ->   case (val1, val2) of-    (ScalarData _, ScalarData _) -> do+    (CASData _, CASData _) -> do+      -- EgisonData instance handles the conversion       r1 <- fromEgison val1 :: EvalM Rational       r2 <- fromEgison val2 :: EvalM Rational       return $ Bool (cmp r1 r2)-    (ScalarData _, _) -> throwErrorWithTrace (TypeMismatch "integer" (Value val2))-    _                 -> throwErrorWithTrace (TypeMismatch "integer" (Value val1))+    (CASData _, _) -> throwErrorWithTrace (TypeMismatch "integer" (Value val2))+    _              -> throwErrorWithTrace (TypeMismatch "integer" (Value val1))  floatCompare :: (forall a. Ord a => a -> a -> Bool) -> String -> PrimitiveFunc floatCompare cmp = twoArgs' $ \val1 val2 ->@@ -172,7 +165,27 @@ truncate' :: String -> PrimitiveFunc truncate' = oneArg $ \val -> numberUnaryOp' val  where-  numberUnaryOp' (ScalarData (Div (Plus []) _))                           = return $ toEgison (0 :: Integer)-  numberUnaryOp' (ScalarData (Div (Plus [Term x []]) (Plus [Term y []]))) = return $ toEgison (quot x y)-  numberUnaryOp' (Float x)                                                = return $ toEgison (truncate x :: Integer)-  numberUnaryOp' val                                                      = throwErrorWithTrace (TypeMismatch "rational or float" (Value val))+  numberUnaryOp' v = case v of+    CASData cv | Just r <- extractRationalCAS cv -> return $ toEgison (truncate r :: Integer)+    Float x -> return $ toEgison (truncate x :: Integer)+    _ -> throwErrorWithTrace (TypeMismatch "rational or float" (Value v))++  -- Extract a Rational from a CASValue if it represents a rational number+  extractRationalCAS :: CASValue -> Maybe Rational+  extractRationalCAS cv = case cv of+    CASInteger n -> Just (n % 1)+    CASPoly [] -> Just 0+    CASPoly [CASTerm coef []] -> extractRationalCAS coef+    CASFrac num den -> do+      n <- extractIntegerCAS num+      d <- extractIntegerCAS den+      if d == 0 then Nothing else Just (n % d)+    _ -> Nothing++  -- Extract an Integer from a CASValue+  extractIntegerCAS :: CASValue -> Maybe Integer+  extractIntegerCAS cv = case cv of+    CASInteger n -> Just n+    CASPoly [] -> Just 0+    CASPoly [CASTerm coef []] -> extractIntegerCAS coef+    _ -> Nothing
hs-src/Language/Egison/Primitives/Types.hs view
@@ -14,7 +14,6 @@ import           Data.Ratio                       ((%))  import           Language.Egison.Data-import           Language.Egison.Math import           Language.Egison.Primitives.Utils  primitiveTypeFunctions :: [(String, EgisonValue)]@@ -37,25 +36,37 @@   -- Note: Other type checking functions (isBool, isScalar, isFloat, isChar, isString,   -- isCollection, isHash, isTensor, typeName) are removed because they are not needed   -- with the static type system. isInteger and isRational are kept because-  -- MathExpr = Integer = Rational in Egison.+  -- MathValue = Integer = Rational in Egison.   ]  -- -- Typing--- Note: Only isInteger and isRational are kept because MathExpr = Integer = Rational in Egison.+-- Note: Only isInteger and isRational are kept because MathValue = Integer = Rational in Egison. -- Other type checking functions are removed as they are not needed with the static type system. --  isInteger :: WHNFData -> EvalM WHNFData-isInteger (Value (ScalarData (Div (Plus []) (Plus [Term 1 []]))))          = return . Value $ Bool True-isInteger (Value (ScalarData (Div (Plus [Term _ []]) (Plus [Term 1 []])))) = return . Value $ Bool True-isInteger _                                                                = return . Value $ Bool False+isInteger (Value val) = case val of+  CASData (CASInteger _) -> return . Value $ Bool True+  CASData (CASPoly [CASTerm (CASInteger _) []]) -> return . Value $ Bool True+  _ -> return . Value $ Bool False+isInteger _ = return . Value $ Bool False  isRational :: WHNFData -> EvalM WHNFData-isRational (Value (ScalarData (Div (Plus []) (Plus [Term _ []]))))          = return . Value $ Bool True-isRational (Value (ScalarData (Div (Plus [Term _ []]) (Plus [Term _ []])))) = return . Value $ Bool True-isRational _                                                                = return . Value $ Bool False+isRational (Value val) = case val of+  CASData cv | isRationalCAS cv -> return . Value $ Bool True+  _ -> return . Value $ Bool False+isRational _ = return . Value $ Bool False +-- | Check if a CASValue represents a rational number (integer or fraction of integers)+isRationalCAS :: CASValue -> Bool+isRationalCAS cv = case cv of+  CASInteger _ -> True+  CASPoly [] -> True  -- zero+  CASPoly [CASTerm coef []] -> isRationalCAS coef+  CASFrac num den -> isRationalCAS num && isRationalCAS den+  _ -> False+ -- -- Transform --@@ -65,9 +76,28 @@ rationalToFloat :: String -> PrimitiveFunc rationalToFloat = oneArg $ \val ->   case val of-    ScalarData (Div (Plus []) _)                           -> return $ Float 0-    ScalarData (Div (Plus [Term x []]) (Plus [Term y []])) -> return $ Float (fromRational (x % y))-    _                                                      -> throwErrorWithTrace (TypeMismatch "integer or rational number" (Value val))+    CASData cv | Just r <- extractRational cv -> return $ Float (fromRational r)+    _ -> throwErrorWithTrace (TypeMismatch "integer or rational number" (Value val))++-- | Extract a Rational from a CASValue if it represents a rational number+extractRational :: CASValue -> Maybe Rational+extractRational cv = case cv of+  CASInteger n -> Just (n % 1)+  CASPoly [] -> Just 0+  CASPoly [CASTerm coef []] -> extractRational coef+  CASFrac num den -> do+    n <- extractInteger num+    d <- extractInteger den+    if d == 0 then Nothing else Just (n % d)+  _ -> Nothing++-- | Extract an Integer from a CASValue if it represents an integer+extractInteger :: CASValue -> Maybe Integer+extractInteger cv = case cv of+  CASInteger n -> Just n+  CASPoly [] -> Just 0+  CASPoly [CASTerm coef []] -> extractInteger coef+  _ -> Nothing  charToInteger :: String -> PrimitiveFunc charToInteger = unaryOp ctoi
hs-src/Language/Egison/Tensor.hs view
@@ -42,9 +42,12 @@ import           Language.Egison.Data import           Language.Egison.Data.Utils import           Language.Egison.IExpr      (Index (..), extractSupOrSubIndex)-import           Language.Egison.Math+import qualified Language.Egison.Math.CAS as CAS import           Language.Egison.RState +-- | Convert an Integer to CASData EgisonValue+intToCASData :: Integer -> EgisonValue+intToCASData n = CASData (CASInteger n)  data IndexM m = IndexM m instance M.Matcher m a => M.Matcher (IndexM m) (Index a)@@ -133,12 +136,36 @@   | V.length xs == 1 = return $ Scalar (xs V.! 0)   | otherwise = throwErrorWithTrace (EgisonBug "sevaral elements in scalar tensor") tref [] t = return t-tref (s@(SupOrSubIndex (ScalarData (SingleSymbol _))):ms) (Tensor (_:ns) xs js) = do-  let yss = split (product ns) xs-  ts <- mapM (\ys -> tref ms (Tensor ns ys (cdr js))) yss-  tConcat s ts-tref (SupOrSubIndex (ScalarData (SingleTerm m [])):ms) t = tIntRef' m t >>= tref ms-tref (SupOrSubIndex (ScalarData ZeroExpr):_) _ = throwError $ Default "tensor index out of bounds: 0"+-- Reject: too many indices for the tensor's rank.+-- E.g. `B_j_k` where B is rank 1 (Vector). Without this, the recursion+-- bottoms out at a scalar with leftover indices and falls through to the+-- generic "must be integer or single symbol" error, which hides the real+-- problem. Give a precise message instead.+tref idxs (Tensor [] _ js) =+  throwError . Default $+    "Too many tensor indices: tensor has rank " ++ show (length js) +++    " but " ++ show (length idxs + length js) ++ " indices given (" +++    show (length idxs) ++ " excess)."+tref (s@(SupOrSubIndex val):ms) (Tensor (_:ns) xs js)+  | isCASSymbol val = do+      let yss = split (product ns) xs+      ts <- mapM (\ys -> tref ms (Tensor ns ys (cdr js))) yss+      tConcat s ts+ where+  isCASSymbol (CASData (CASPoly [CASTerm (CASInteger 1) [(CAS.Symbol _ _ _, 1)]])) = True+  isCASSymbol _ = False+tref (SupOrSubIndex val:ms) t+  | Just m <- extractCASInteger val = tIntRef' m t >>= tref ms+ where+  extractCASInteger (CASData (CASPoly [CASTerm (CASInteger m) []])) = Just m+  extractCASInteger (CASData (CASInteger m)) = Just m+  extractCASInteger _ = Nothing+tref (SupOrSubIndex val:_) _+  | isCASZero val = throwError $ Default "tensor index out of bounds: 0"+ where+  isCASZero (CASData (CASInteger 0)) = True+  isCASZero (CASData (CASPoly [])) = True+  isCASZero _ = False tref (s@(SupOrSubIndex (Tuple [mVal, nVal])):ms) t@(Tensor is _ _) = do   m <- fromEgison mVal   n <- fromEgison nVal@@ -148,7 +175,7 @@     else do       ts <- mapM (\i -> tIntRef' i t >>= tref ms) [m..n]       symId <- fresh-      let index = symbolScalarData "" (":::" ++ symId)+      let index = symbolCASData "" (":::" ++ symId)       case s of         Sub{}    -> tConcat (Sub index) ts         Sup{}    -> tConcat (Sup index) ts@@ -273,7 +300,10 @@   rts1 <- mapM (`tIntRef` t1') (enumTensorIndices cns)   rts2 <- mapM (`tIntRef` t2') (enumTensorIndices cns)   rts' <- zipWithM (tProduct f) rts1 rts2-  let ret = Tensor (cns ++ tShape (head rts')) (V.concat (map tToVector rts')) (cjs ++ tIndex (head rts'))+  let firstRT = case rts' of+                  (r:_) -> r+                  []    -> error "tProduct: rts' empty (impossible)"+  let ret = Tensor (cns ++ tShape firstRT) (V.concat (map tToVector rts')) (cjs ++ tIndex firstRT)   tTranspose (uniq (tDiagIndex (js1 ++ js2))) ret >>= removeDFFromTensor  where   uniq :: [Index EgisonValue] -> [Index EgisonValue]@@ -334,7 +364,10 @@                               rt2 <- tIntRef is t2'                               tProduct f rt1 rt2)                    (enumTensorIndices cns1)-      let ret = Tensor (cns1 ++ tShape (head rts')) (V.concat (map tToVector rts')) (map toSupSub cjs1 ++ tIndex (head rts'))+      let firstRT = case rts' of+                      (r:_) -> r+                      []    -> error "tDiag: rts' empty (impossible)"+      let ret = Tensor (cns1 ++ tShape firstRT) (V.concat (map tToVector rts')) (map toSupSub cjs1 ++ tIndex firstRT)       tTranspose (uniq (map toSupSub cjs1 ++ tjs1 ++ tjs2)) ret >>= removeDFFromTensor  where   h :: [Index EgisonValue] -> [Index EgisonValue] -> ([Index EgisonValue], [Index EgisonValue], [Index EgisonValue], [Index EgisonValue])@@ -366,8 +399,8 @@   match dfs js (List M.Something)     [ [mc| $hjs ++ $a : $mjs ++ ?(p a) : $tjs -> do              let m = fromIntegral (length hjs)-             xs' <- mapM (\i -> tref (hjs ++ (Sub (ScalarData (SingleTerm i [])) : mjs)-                                          ++ (Sub (ScalarData (SingleTerm i [])) : tjs)) t)+             xs' <- mapM (\i -> tref (hjs ++ (Sub (intToCASData i) : mjs)+                                          ++ (Sub (intToCASData i) : tjs)) t)                          [1..(ns !! m)]              tConcat a xs' >>= tTranspose (hjs ++ a : mjs ++ tjs) >>= tContract' |]     , [mc| _ -> return t |]
hs-src/Language/Egison/Type/Check.hs view
@@ -68,13 +68,66 @@      -- Primitives from Primitives.hs (strictPrimitives and lazyPrimitives)     primitivesTypes =-      [ ("addSubscript", binOp TInt TInt TInt)  -- MathExpr operations-      , ("addSuperscript", binOp TInt TInt TInt)  -- MathExpr operations+      [ ("addSubscript", binOp TInt TInt TInt)  -- MathValue operations+      , ("addSuperscript", binOp TInt TInt TInt)  -- MathValue operations       , ("assert", binOp TString TBool TBool)       , ("assertEqual", forallA $ ternOpT TString (TVar a) (TVar a) TBool)       , ("sortWithSign", Forall [] [] $ TFun (TCollection (TCollection TInt)) (TTuple [TInt, TCollection TInt]))-      , ("updateFunctionArgs", Forall [] [] $ TFun TMathExpr (TFun (TCollection TMathExpr) TMathExpr))+      , ("updateFunctionArgs", Forall [] [] $ TFun TMathValue (TFun (TCollection TMathValue) TMathValue))+      , ("functionSymbol", Forall [] [] $ TFun TString (TFun (TCollection TMathValue) TMathValue))+      , ("symbolIndices", Forall [] [] $+          TFun TMathValue (TCollection (TInductive "TensorIndex" [])))+      , ("requireAnalyticDerivative", Forall [] [] $+          TFun TMathValue (TFun TMathValue TMathValue))+      , ("quoteScalar", Forall [] [] $ TFun TMathValue TMathValue)+      , ("mathFunctionName", forallA $ TFun (TVar a) TString)+      , ("casTerms", Forall [] [] $ TFun TMathValue (TCollection TMathValue))+      , ("casFromTerms", Forall [] [] $ TFun (TCollection TMathValue) TMathValue)+      , ("termCoeff", Forall [] [] $ TFun TMathValue TMathValue)+      , ("termMonomial", Forall [] [] $ TFun TMathValue (TCollection (TTuple [TMathValue, TMathValue])))+      , ("typeOf", forallA $ TFun (TVar a) TString)+      , ("inspect", forallA $ TFun (TVar a) TString)+      -- Unsafe cast for cas-quotient generated code only (M4;+      -- design/type-cas-quotient.md): projQ/reprQ cross between a+      -- quotient's nominal type and its base representation.+      , ("casQuotientCast", Forall [TyVar "a", TyVar "b"] []+          (TFun (TVar (TyVar "a")) (TVar (TyVar "b"))))+      , ("differentialClosed", Forall [] [] $ TFun TMathValue (TFun TMathValue TBool))+      , ("isInPolyAtoms", Forall [] [] $ TFun TMathValue (TFun (TCollection TString) TBool))+      , ("isPureInteger", forallA $ TFun (TVar a) TBool)+      , ("isPureFraction", forallA $ TFun (TVar a) TBool)+      , ("numReductionRules", Forall [] [] $ TFun (TTuple []) TInt)+      , ("numDerivativeRules", Forall [] [] $ TFun (TTuple []) TInt)+      , ("ruleNames", Forall [] [] $ TFun (TTuple []) (TCollection TString))+      , ("derivativeNames", Forall [] [] $ TFun (TTuple []) (TCollection TString))+      , ("hasReductionRule", Forall [] [] $ TFun TString TBool)+      , ("hasDerivativeRule", Forall [] [] $ TFun TString TBool)+      , ("applyTermRule", Forall [] [] $+          TFun TMathValue (TFun TMathValue (TFun TMathValue TMathValue)))+      -- Trigger-symbol pre-filter for `declare rule auto`. True iff the+      -- value tree references at least one of the named symbols/functions.+      , ("containsAnySymbol", Forall [] [] $+          TFun (TCollection TString) (TFun TMathValue TBool))+      -- CAS-specialised iterateRules used by `mathNormalize`. Trigger sets+      -- live in EvalState and are read inside the primitive, so only the+      -- rule list and value are parameters.+      , ("iterateRulesCAS", Forall [] [] $+          TFun (TCollection (TFun TMathValue TMathValue))+               (TFun TMathValue TMathValue))+      -- Per-term trigger guard for generated pattern-rule steps.+      , ("casContainsAnySymbol", Forall [] [] $+          TFun (TCollection TString) (TFun TMathValue TBool))+      -- Phase A.5 deep-traversal primitives.+      -- Type: (MathValue -> MathValue) -> MathValue -> MathValue.+      , ("mapPolyAll", Forall [] [] $+          TFun (TFun TMathValue TMathValue) (TFun TMathValue TMathValue))+      , ("mapTermAll", Forall [] [] $+          TFun (TFun TMathValue TMathValue) (TFun TMathValue TMathValue))+      , ("mapFracAll", Forall [] [] $+          TFun (TFun TMathValue TMathValue) (TFun TMathValue TMathValue))       , ("tensorShape", forallA $ TFun (TTensor (TVar a)) (TCollection TInt))+      , ("tensorIndices", forallA $+          TFun (TTensor (TVar a)) (TCollection (TInductive "TensorIndex" [])))       , ("tensorToList", forallA $ TFun (TTensor (TVar a)) (TCollection (TVar a)))       , ("dfOrder", forallA $ TFun (TTensor (TVar a)) TInt)       ]@@ -96,9 +149,7 @@       -- Fraction operations       , ("numerator", unaryOp TInt TInt)       , ("denominator", unaryOp TInt TInt)-      -- MathExpr operations-      , ("fromMathExpr", unaryOp TInt (TInductive "MathExpr'" []))-      , ("toMathExpr'", unaryOp (TInductive "MathExpr'" []) TInt)+      -- MathValue operations       , ("symbolNormalize", unaryOp TInt TInt)       -- Integer operations       , ("i.modulo", binOp TInt TInt TInt)@@ -230,4 +281,3 @@       -- and other algebraicDataMatcher constructors are now automatically registered       -- when the matcher is defined via registerAlgebraicConstructors       ]-
hs-src/Language/Egison/Type/Env.hs view
@@ -35,7 +35,8 @@   , patternEnvToList   ) where -import           Data.List                  (sortOn)+import           Data.List                  (sortBy, sortOn)+import           Data.Ord                   (Down(..)) import           Data.Map.Strict            (Map) import qualified Data.Map.Strict            as Map import           Data.Set                   (Set)@@ -45,7 +46,7 @@ import           Language.Egison.VarEntry   (VarEntry(..)) import           Language.Egison.Type.Types (TyVar (..), Type (..), TypeScheme (..),                                              Constraint(..), ClassInfo(..), InstanceInfo(..),-                                             freeTyVars, freshTyVar)+                                             freeTyVars, freshTyVar, substTyVar)  -- | Type environment: uses same data structure as evaluation environment -- Maps base variable names to all bindings with that name@@ -130,8 +131,10 @@       length stored < length target &&       stored == take (length stored) target     +    -- Sort by descending index length, preserving insertion order for equal lengths+    -- so that local bindings (added later via extendEnv) come before global ones     sortByIndexLengthDesc :: [VarEntry TypeScheme] -> [VarEntry TypeScheme]-    sortByIndexLengthDesc = reverse . sortOn (length . veIndices)+    sortByIndexLengthDesc = sortBy (\a b -> compare (Down (length (veIndices a))) (Down (length (veIndices b))))          -- Check if target is a prefix of candidate (for prefix matching)     -- Example: [a] is prefix of [i, j]@@ -184,38 +187,13 @@ instantiate :: TypeScheme -> Int -> ([Constraint], Type, Int) instantiate (Forall vs cs t) counter =   let freshVars = zipWith (\v i -> (v, TVar (freshTyVar "t" (counter + i)))) vs [0..]-      substType = foldr (\(old, new) acc -> substVar old new acc) t freshVars+      substType = foldr (\(old, new) acc -> substTyVar old new acc) t freshVars       substCs = map (substConstraint freshVars) cs   in (substCs, substType, counter + length vs)   where     substConstraint :: [(TyVar, Type)] -> Constraint -> Constraint-    substConstraint vars (Constraint cls ty) =-      Constraint cls (foldr (\(old, new) acc -> substVar old new acc) ty vars)-    substVar :: TyVar -> Type -> Type -> Type-    substVar _ _ TInt = TInt-    substVar _ _ TMathExpr = TMathExpr-    substVar _ _ TPolyExpr = TPolyExpr-    substVar _ _ TTermExpr = TTermExpr-    substVar _ _ TSymbolExpr = TSymbolExpr-    substVar _ _ TIndexExpr = TIndexExpr-    substVar _ _ TFloat = TFloat-    substVar _ _ TBool = TBool-    substVar _ _ TChar = TChar-    substVar _ _ TString = TString-    substVar old new (TVar v)-      | v == old = new-      | otherwise = TVar v-    substVar old new (TTuple ts) = TTuple (map (substVar old new) ts)-    substVar old new (TCollection t') = TCollection (substVar old new t')-    substVar old new (TInductive name ts) = TInductive name (map (substVar old new) ts)-    substVar old new (TTensor t') = TTensor (substVar old new t')-    substVar old new (THash k v) = THash (substVar old new k) (substVar old new v)-    substVar old new (TMatcher t') = TMatcher (substVar old new t')-    substVar old new (TFun t1 t2) = TFun (substVar old new t1) (substVar old new t2)-    substVar old new (TIO t') = TIO (substVar old new t')-    substVar old new (TIORef t') = TIORef (substVar old new t')-    substVar _ _ TPort = TPort-    substVar _ _ TAny = TAny+    substConstraint vars (Constraint cls tys) =+      Constraint cls (map (\ty -> foldr (\(old, new) acc -> substTyVar old new acc) ty vars) tys)  -------------------------------------------------------------------------------- -- Class Environment
hs-src/Language/Egison/Type/Error.hs view
@@ -20,11 +20,12 @@   , withContext   ) where -import           Data.List                  (intercalate)+import           Data.List                  (intercalate, nub) import           GHC.Generics               (Generic)  import           Language.Egison.Type.Index (IndexSpec)-import           Language.Egison.Type.Types (TensorShape (..), TyVar (..), Type (..))+import           Language.Egison.Type.Types (TensorShape (..), TyVar (..), Type (..), SymbolSet(..), prettyTypeAtomValue,+                                             Constraint (..), constraintClass, constraintTypes)  -- | Source location information data SourceLocation = SourceLocation@@ -68,6 +69,22 @@     -- ^ Expression type cannot be inferred (treated as Any)   | DeprecatedFeatureWarning String TypeErrorContext     -- ^ Feature is deprecated+  | MatcherCoverageWarning Type [String] TypeErrorContext+    -- ^ A @matcher@ lacks a general clause for some pattern constructor(s) of its matched+    --   type (paper Coverage, Def 4.2(3)): the matched type, then the missing constructors.+  | MatcherNextMatcherWarning Type String TypeErrorContext+    -- ^ A bare-variable next matcher (rendered) at a constructor-/concrete-headed hole (the+    --   hole's type) is not structurally admissible (paper PP-Con, Def 4.2(1a)).+  | ClassMethodShadowWarning String String TypeErrorContext+    -- ^ A top-level definition reuses a class method name (method name, class name).+    --   The definition replaces the dispatching binding, so the method stops+    --   dispatching on its argument type everywhere after this point.+  | ForwardReferenceWarning String TypeErrorContext+    -- ^ An unbound name that IS a definition of the current load unit — i.e. it+    --   is defined later than this reference.  Falls back to Any exactly like+    --   UnboundVariableWarning, but tells the user the actual fix: signatures+    --   are collected in a prepass, so annotating the referenced definition+    --   makes the forward reference typable.   deriving (Eq, Show, Generic)  -- | Type errors@@ -96,6 +113,27 @@     -- ^ Inferred type doesn't match annotation   | UnsupportedFeature String TypeErrorContext     -- ^ Feature not yet implemented+  | MissingSignatureConstraint String [Constraint] TypeErrorContext+    -- ^ A definition's body requires type-class constraints on the+    -- signature's type variables that the signature does not declare+  | PatternFunctionLinearityError String [String] [String] TypeErrorContext+    -- ^ Pattern function parameter-linearity violation (paper PATFUN-DEF side+    --   condition): each parameter must be used exactly once in the body, in+    --   declaration order.  Fields: function name, declared parameters, actual+    --   parameter uses in body order.+  | PatternFunctionParamUnderBranchError String [String] TypeErrorContext+    -- ^ Pattern function parameters used under a branching or repeating pattern+    --   (or-, loop-, not-, forall-pattern): such an occurrence may be expanded+    --   zero or several times along a matching path, breaking the binding+    --   contract.  Fields: function name, offending parameters.+  | MatcherDataArmsNotExhaustive String Type TypeErrorContext+    -- ^ A @matcher@ clause (rendered pp pattern) of a matcher for the given matched type+    --   whose primitive-data-pattern arms are not exhaustive (paper Def 4.2(1c), arm+    --   exhaustiveness): a target that matches the clause's pattern but none of its arms+    --   raises "Primitive data pattern match failed" at runtime instead of backtracking.+    --   Checked as a conservative syntactic approximation (see 'pdArmsExhaustive' in the+    --   inference module); the standard-library convention is a final @| _ -> []@ (or+    --   @| $tgt -> ...@) arm.   deriving (Eq, Show, Generic)  @@ -163,6 +201,35 @@     formatWithContext ctx $       "Unsupported feature: " ++ feature +  MissingSignatureConstraint name cs ctx ->+    formatWithContext ctx $+      "The body of '" ++ name ++ "' requires type class constraints that its signature does not declare:\n" +++      "  Missing: " ++ intercalate ", " (map prettyConstraint' cs) ++ "\n" +++      "  Declare them in the signature, e.g. def " ++ name ++ " {" +++        intercalate ", " (map prettyConstraint' cs) ++ "} ..."+    where prettyConstraint' c =+            constraintClass c ++ concatMap ((' ' :) . prettyType) (constraintTypes c)++  PatternFunctionLinearityError name params uses ctx ->+    formatWithContext ctx $+      "Pattern function '" ++ name ++ "' must use each parameter exactly once, in declaration order:\n" +++      "  Parameters:    " ++ intercalate ", " (map ("~" ++) params) ++ "\n" +++      "  Uses in body:  " ++ (if null uses then "(none)" else intercalate ", " (map ("~" ++) uses))++  PatternFunctionParamUnderBranchError name offenders ctx ->+    formatWithContext ctx $+      "Pattern function '" ++ name ++ "' uses parameters under a branching or repeating pattern\n" +++      "(or-, loop-, not-, or forall-pattern), where they may be expanded zero or several times:\n" +++      "  Parameters:  " ++ intercalate ", " (map ("~" ++) offenders)++  MatcherDataArmsNotExhaustive ppStr ty ctx ->+    formatWithContext ctx $+      "Matcher clause `" ++ ppStr ++ "` (matcher for " ++ displayType ty ++ ")" +++      " has non-exhaustive data-pattern arms: a target that matches the clause's pattern but" +++      " none of its arms fails at runtime (\"Primitive data pattern match failed\");" +++      " end the arms with `| _ -> []`" +++      "\n  (arm exhaustiveness; paper Def 4.2(1c))"+ -- | Format error with context formatWithContext :: TypeErrorContext -> String -> String formatWithContext ctx msg =@@ -209,10 +276,86 @@     formatWithContext ctx $       "Warning: Deprecated feature: " ++ feature +  MatcherCoverageWarning ty missing ctx ->+    formatWithContext ctx $+      "Warning: matcher for " ++ displayType ty +++      " has no general clause for pattern constructor(s): " ++ intercalate ", " missing +++      "\n  (a pattern using such a constructor would get stuck at runtime; paper Coverage, Def 4.2(3))"++  MatcherNextMatcherWarning holeTy comp ctx ->+    formatWithContext ctx $+      "Warning: the next matcher `" ++ comp ++ "` is a bare-variable matcher, not structurally" +++      " admissible at a constructor-headed hole of type " ++ displayType holeTy +++      "\n  (a constructor pattern there would get stuck at runtime; paper PP-Con, Def 4.2(1a))"++  ClassMethodShadowWarning name cls ctx ->+    formatWithContext ctx $+      "Warning: '" ++ name ++ "' is a method of class '" ++ cls ++ "'," +++      " and this top-level definition shadows it" +++      "\n  ('" ++ name ++ "' no longer dispatches on its argument type anywhere after" +++      " this point; rename the definition)"++  ForwardReferenceWarning name ctx ->+    formatWithContext ctx $+      "Warning: '" ++ name ++ "' is defined later in this load unit and is not" +++      " visible here yet (assuming type 'Any')" +++      "\n  (type signatures are collected before inference, so adding one to '" +++      name ++ "' makes this forward reference typable)"++-- | Pretty print a type after renaming its type variables, in order of first+-- appearance, to @a@, @b@, @c@, ...  Inference-internal names such as @t143@+-- carry no information for the user.  Used by the matcher diagnostics, which+-- each show a single type, so the renaming cannot break cross-references+-- between types (unification errors, which relate two types, are shown with+-- their original variable names).+displayType :: Type -> String+displayType = prettyType . renameVarsForDisplay++renameVarsForDisplay :: Type -> Type+renameVarsForDisplay ty = mapVars ty+  where+    mapping = zip (nub (collect ty))+                  ([TyVar [c] | c <- ['a'..'z']] ++ [TyVar ('a' : show i) | i <- [1 :: Int ..]])+    sub v = case lookup v mapping of+      Just v' -> v'+      Nothing -> v+    mapVars t = case t of+      TVar v             -> TVar (sub v)+      TTuple ts          -> TTuple (map mapVars ts)+      TCollection t1     -> TCollection (mapVars t1)+      TInductive n ts    -> TInductive n (map mapVars ts)+      TTensor t1         -> TTensor (mapVars t1)+      THash t1 t2        -> THash (mapVars t1) (mapVars t2)+      TMatcher t1        -> TMatcher (mapVars t1)+      TMatcherSlot t1 t2 -> TMatcherSlot (mapVars t1) (mapVars t2)+      TFun t1 t2         -> TFun (mapVars t1) (mapVars t2)+      TIO t1             -> TIO (mapVars t1)+      TIORef t1          -> TIORef (mapVars t1)+      TTerm t1 ss        -> TTerm (mapVars t1) ss+      TFrac t1           -> TFrac (mapVars t1)+      TPoly t1 ss        -> TPoly (mapVars t1) ss+      _                  -> t+    collect t = case t of+      TVar v             -> [v]+      TTuple ts          -> concatMap collect ts+      TCollection t1     -> collect t1+      TInductive _ ts    -> concatMap collect ts+      TTensor t1         -> collect t1+      THash t1 t2        -> collect t1 ++ collect t2+      TMatcher t1        -> collect t1+      TMatcherSlot t1 t2 -> collect t1 ++ collect t2+      TFun t1 t2         -> collect t1 ++ collect t2+      TIO t1             -> collect t1+      TIORef t1          -> collect t1+      TTerm t1 _         -> collect t1+      TFrac t1           -> collect t1+      TPoly t1 _         -> collect t1+      _                  -> []+ -- | Pretty print a type prettyType :: Type -> String prettyType TInt = "Integer"-prettyType TMathExpr = "MathExpr"+prettyType TMathValue = "MathValue" prettyType TPolyExpr = "PolyExpr" prettyType TTermExpr = "TermExpr" prettyType TSymbolExpr = "SymbolExpr"@@ -229,11 +372,23 @@ prettyType (TTensor t) = "Tensor " ++ prettyType t prettyType (THash k v) = "Hash " ++ prettyType k ++ " " ++ prettyType v prettyType (TMatcher t) = "Matcher " ++ prettyType t+prettyType (TMatcherSlot s t) = "MatcherSlot " ++ prettyType s ++ " " ++ prettyType t prettyType (TFun t1 t2) = prettyType t1 ++ " -> " ++ prettyType t2 prettyType (TIO t) = "IO " ++ prettyType t prettyType (TIORef t) = "IORef " ++ prettyType t prettyType TPort = "Port" prettyType TAny = "_"+-- New CAS types+prettyType TFactor = "Factor"+prettyType (TTerm t ss) = "Term " ++ prettyType t ++ " " ++ prettySymbolSet ss+prettyType (TFrac t) = "Frac " ++ prettyType t+prettyType (TPoly t ss) = "Poly " ++ prettyType t ++ " " ++ prettySymbolSet ss++-- | Pretty print a SymbolSet (local helper for type errors)+prettySymbolSet :: SymbolSet -> String+prettySymbolSet (SymbolSetClosed syms) = "[" ++ intercalate ", " (map prettyTypeAtomValue syms) ++ "]"+prettySymbolSet SymbolSetOpen = "[..]"+prettySymbolSet (SymbolSetVar (TyVar v)) = v  -- | Pretty print a tensor shape prettyShape :: TensorShape -> String
hs-src/Language/Egison/Type/Infer.hs view
@@ -50,3248 +50,4249 @@   , clearWarnings   ) where -import           Control.Monad              (foldM, zipWithM)-import           Control.Monad.Except       (ExceptT, runExceptT, throwError)-import           Control.Monad.State.Strict (StateT, evalStateT, runStateT, get, gets, modify, put)-import           Data.List                  (isPrefixOf, nub, partition)-import           Data.Maybe                  (catMaybes)-import qualified Data.Map.Strict             as Map-import qualified Data.Set                    as Set-import           Language.Egison.AST        (ConstantExpr (..), PrimitivePatPattern (..))-import           Language.Egison.IExpr      (IExpr (..), ITopExpr (..), TITopExpr (..)-                                            , TIExpr (..), TIExprNode (..)-                                            , IBindingExpr, TIBindingExpr-                                            , IMatchClause, TIMatchClause, IPatternDef, TIPatternDef-                                            , IPattern (..), ILoopRange (..)-                                            , TIPattern (..), TIPatternNode (..), TILoopRange (..)-                                            , IPrimitiveDataPattern, PDPatternBase (..)-                                            , extractNameFromVar, Var (..), Index (..), stringToVar-                                            , tiExprType)-import           Language.Egison.Pretty     (prettyStr)-import           Language.Egison.Type.Env-import qualified Language.Egison.Type.Error as TE-import           Language.Egison.Type.Error (TypeError(..), TypeErrorContext(..), TypeWarning(..),-                                              emptyContext, withExpr)-import           Language.Egison.Type.Subst (Subst(..), applySubst, applySubstConstraint,-                                              applySubstScheme, composeSubst, emptySubst)-import           Language.Egison.Type.Tensor (normalizeTensorType)-import           Language.Egison.Type.Types-import qualified Language.Egison.Type.Types as Types-import           Language.Egison.Type.Unify as TU-import qualified Language.Egison.Type.Unify as Unify-import           Language.Egison.Type.Instance (findMatchingInstanceForType)------------------------------------------------------------------------------------- * Infer Monad and State------------------------------------------------------------------------------------- | Inference configuration-data InferConfig = InferConfig-  { cfgPermissive      :: Bool  -- ^ Treat unbound variables as warnings, not errors-  , cfgCollectWarnings :: Bool  -- ^ Collect warnings during inference-  }--instance Show InferConfig where-  show cfg = "InferConfig { cfgPermissive = " ++ show (cfgPermissive cfg)-           ++ ", cfgCollectWarnings = " ++ show (cfgCollectWarnings cfg)-           ++ " }"---- | Default configuration (strict mode)-defaultInferConfig :: InferConfig-defaultInferConfig = InferConfig-  { cfgPermissive = False-  , cfgCollectWarnings = False-  }---- | Permissive configuration (for gradual adoption)-permissiveInferConfig :: InferConfig-permissiveInferConfig = InferConfig-  { cfgPermissive = True-  , cfgCollectWarnings = True-  }---- | Inference state-data InferState = InferState-  { inferCounter     :: Int              -- ^ Fresh variable counter-  , inferEnv         :: TypeEnv          -- ^ Current type environment-  , inferWarnings    :: [TypeWarning]    -- ^ Collected warnings-  , inferConfig      :: InferConfig      -- ^ Configuration-  , inferClassEnv    :: ClassEnv         -- ^ Type class environment-  , inferPatternEnv  :: PatternTypeEnv   -- ^ Pattern constructor environment (merged)-  , inferPatternFuncEnv :: PatternTypeEnv  -- ^ Pattern function environment (for disambiguation)-  , inferConstraints :: [Constraint]     -- ^ Accumulated type class constraints-  , declaredSymbols  :: Map.Map String Type  -- ^ Declared symbols with their types-  } deriving (Show)---- | Initial inference state-initialInferState :: InferState-initialInferState = InferState 0 emptyEnv [] defaultInferConfig emptyClassEnv emptyPatternEnv emptyPatternEnv [] Map.empty---- | Create initial state with config-initialInferStateWithConfig :: InferConfig -> InferState-initialInferStateWithConfig cfg = InferState 0 emptyEnv [] cfg emptyClassEnv emptyPatternEnv emptyPatternEnv [] Map.empty---- | Inference monad (with IO for potential future extensions)-type Infer a = ExceptT TypeError (StateT InferState IO) a---- | Run type inference-runInfer :: Infer a -> InferState -> IO (Either TypeError a)-runInfer m st = evalStateT (runExceptT m) st---- | Run type inference and also return warnings-runInferWithWarnings :: Infer a -> InferState -> IO (Either TypeError a, [TypeWarning])-runInferWithWarnings m st = do-  (result, finalState) <- runStateT (runExceptT m) st-  return (result, inferWarnings finalState)---- | Run inference and return result, warnings, and final state-runInferWithWarningsAndState :: Infer a -> InferState -> IO (Either TypeError a, [TypeWarning], InferState)-runInferWithWarningsAndState m st = do-  (result, finalState) <- runStateT (runExceptT m) st-  return (result, inferWarnings finalState, finalState)------------------------------------------------------------------------------------- * Helper Functions------------------------------------------------------------------------------------- | Add a warning-addWarning :: TypeWarning -> Infer ()-addWarning w = modify $ \st -> st { inferWarnings = w : inferWarnings st }---- | Clear all accumulated warnings-clearWarnings :: Infer ()-clearWarnings = modify $ \st -> st { inferWarnings = [] }---- | Add type class constraints (with deduplication)-addConstraints :: [Constraint] -> Infer ()-addConstraints cs = modify $ \st ->-  let existing = inferConstraints st-      -- Only add constraints that are not already present-      newConstraints = filter (`notElem` existing) cs-  in st { inferConstraints = existing ++ newConstraints }---- | Get accumulated constraints-getConstraints :: Infer [Constraint]-getConstraints = inferConstraints <$> get---- | Clear accumulated constraints-clearConstraints :: Infer ()-clearConstraints = modify $ \st -> st { inferConstraints = [] }---- | Run an action with local constraint tracking-withLocalConstraints :: Infer a -> Infer (a, [Constraint])-withLocalConstraints action = do-  oldConstraints <- getConstraints-  clearConstraints-  result <- action-  newConstraints <- getConstraints-  modify $ \st -> st { inferConstraints = oldConstraints }-  return (result, newConstraints)---- | Check if we're in permissive mode-isPermissive :: Infer Bool-isPermissive = cfgPermissive . inferConfig <$> get---- | Generate a fresh type variable-freshVar :: String -> Infer Type-freshVar prefix = do-  st <- get-  let n = inferCounter st-  put st { inferCounter = n + 1 }-  return $ TVar $ TyVar $ prefix ++ show n---- | Get the current type environment-getEnv :: Infer TypeEnv-getEnv = inferEnv <$> get---- | Set the type environment-setEnv :: TypeEnv -> Infer ()-setEnv env = modify $ \st -> st { inferEnv = env }---- | Get the current pattern type environment-getPatternEnv :: Infer PatternTypeEnv-getPatternEnv = inferPatternEnv <$> get---- | Set the pattern type environment-setPatternEnv :: PatternTypeEnv -> Infer ()-setPatternEnv penv = modify $ \st -> st { inferPatternEnv = penv }---- | Get the current pattern function environment (for disambiguation)-getPatternFuncEnv :: Infer PatternTypeEnv-getPatternFuncEnv = inferPatternFuncEnv <$> get---- | Set the pattern function environment-setPatternFuncEnv :: PatternTypeEnv -> Infer ()-setPatternFuncEnv penv = modify $ \st -> st { inferPatternFuncEnv = penv }---- | Get the current class environment-getClassEnv :: Infer ClassEnv-getClassEnv = inferClassEnv <$> get---- | Resolve a constraint based on available instances--- If the constraint type is a Tensor type and no instance exists for it,--- try to use the element type's instance instead--- | Resolve constraints in a TIExpr recursively-resolveConstraintsInTIExpr :: ClassEnv -> Subst -> TIExpr -> TIExpr-resolveConstraintsInTIExpr classEnv subst (TIExpr (Forall vars constraints ty) node) =-  let resolvedConstraints = map (resolveConstraintWithInstances classEnv subst) constraints-      resolvedNode = resolveConstraintsInNode classEnv subst node-  in TIExpr (Forall vars resolvedConstraints ty) resolvedNode---- | Resolve constraints in a TIExprNode recursively-resolveConstraintsInNode :: ClassEnv -> Subst -> TIExprNode -> TIExprNode-resolveConstraintsInNode classEnv subst node = case node of-  TIConstantExpr c -> TIConstantExpr c-  TIVarExpr name -> TIVarExpr name-  TILambdaExpr mVar params body ->-    TILambdaExpr mVar params (resolveConstraintsInTIExpr classEnv subst body)-  TIApplyExpr func args ->-    TIApplyExpr (resolveConstraintsInTIExpr classEnv subst func)-                (map (resolveConstraintsInTIExpr classEnv subst) args)-  TITupleExpr exprs ->-    TITupleExpr (map (resolveConstraintsInTIExpr classEnv subst) exprs)-  TICollectionExpr exprs ->-    TICollectionExpr (map (resolveConstraintsInTIExpr classEnv subst) exprs)-  TIIfExpr cond thenExpr elseExpr ->-    TIIfExpr (resolveConstraintsInTIExpr classEnv subst cond)-             (resolveConstraintsInTIExpr classEnv subst thenExpr)-             (resolveConstraintsInTIExpr classEnv subst elseExpr)-  TILetExpr bindings body ->-    TILetExpr (map (\(p, e) -> (p, resolveConstraintsInTIExpr classEnv subst e)) bindings)-              (resolveConstraintsInTIExpr classEnv subst body)-  TILetRecExpr bindings body ->-    TILetRecExpr (map (\(p, e) -> (p, resolveConstraintsInTIExpr classEnv subst e)) bindings)-                 (resolveConstraintsInTIExpr classEnv subst body)-  TIIndexedExpr override expr indices ->-    TIIndexedExpr override (resolveConstraintsInTIExpr classEnv subst expr) -                  (fmap (resolveConstraintsInTIExpr classEnv subst) <$> indices)-  TIGenerateTensorExpr func shape ->-    TIGenerateTensorExpr (resolveConstraintsInTIExpr classEnv subst func)-                         (resolveConstraintsInTIExpr classEnv subst shape)-  TITensorExpr shape elems ->-    TITensorExpr (resolveConstraintsInTIExpr classEnv subst shape)-                 (resolveConstraintsInTIExpr classEnv subst elems)-  TITensorContractExpr tensor ->-    TITensorContractExpr (resolveConstraintsInTIExpr classEnv subst tensor)-  TITensorMapExpr func tensor ->-    TITensorMapExpr (resolveConstraintsInTIExpr classEnv subst func)-                    (resolveConstraintsInTIExpr classEnv subst tensor)-  TITensorMap2Expr func t1 t2 ->-    TITensorMap2Expr (resolveConstraintsInTIExpr classEnv subst func)-                     (resolveConstraintsInTIExpr classEnv subst t1)-                     (resolveConstraintsInTIExpr classEnv subst t2)-  TIMatchExpr mode target matcher clauses ->-    TIMatchExpr mode-                (resolveConstraintsInTIExpr classEnv subst target)-                (resolveConstraintsInTIExpr classEnv subst matcher)-                (map (\(p, e) -> (p, resolveConstraintsInTIExpr classEnv subst e)) clauses)-  _ -> node--resolveConstraintWithInstances :: ClassEnv -> Subst -> Constraint -> Constraint-resolveConstraintWithInstances classEnv subst (Constraint className tyVar) =-  let resolvedType = applySubst subst tyVar-      instances = lookupInstances className classEnv-  in case resolvedType of-       TTensor elemType ->-         -- For Tensor types, search for an instance-         case findMatchingInstanceForType resolvedType instances of-           Just _ -> -             -- If Tensor itself has an instance, use it-             Constraint className resolvedType-           Nothing -> -             -- If Tensor has no instance, use the element type's constraint-             -- This assumes tensorMap will apply element-wise-             -- Use element type's constraint even if no instance is found for it-             -- (Error will be detected in a later phase)-             Constraint className elemType-       _ -> -         -- For non-Tensor types, simply apply the substitution-         Constraint className resolvedType---- | Extend the environment temporarily-withEnv :: [(String, TypeScheme)] -> Infer a -> Infer a-withEnv bindings action = do-  oldEnv <- getEnv-  setEnv $ extendEnvMany (map (\(name, scheme) -> (stringToVar name, scheme)) bindings) oldEnv-  result <- action-  setEnv oldEnv-  return result---- | Look up a variable's type-lookupVar :: String -> Infer Type-lookupVar name = do-  env <- getEnv-  case lookupEnv (stringToVar name) env of-    Just scheme -> do-      st <- get-      let (constraints, t, newCounter) = instantiate scheme (inferCounter st)-      -- Track constraints for type class resolution-      modify $ \s -> s { inferCounter = newCounter }-      addConstraints constraints-      return t-    Nothing -> do-      -- Check if this is a declared symbol-      st <- get-      case Map.lookup name (declaredSymbols st) of-        Just ty -> return ty  -- Return the declared type without warning-        Nothing -> do-          permissive <- isPermissive-          if permissive-            then do-              -- In permissive mode, treat as a warning and return a fresh type variable-              addWarning $ UnboundVariableWarning name emptyContext-              freshVar "unbound"-            else throwError $ UnboundVariable name emptyContext---- | Lookup variable and return type with constraints-lookupVarWithConstraints :: String -> Infer (Type, [Constraint])-lookupVarWithConstraints name = do-  env <- getEnv-  case lookupEnv (stringToVar name) env of-    Just scheme -> do-      st <- get-      let (constraints, t, newCounter) = instantiate scheme (inferCounter st)-      -- Track constraints for type class resolution-      modify $ \s -> s { inferCounter = newCounter }-      addConstraints constraints-      return (t, constraints)-    Nothing -> do-      -- Check if this is a declared symbol-      st <- get-      case Map.lookup name (declaredSymbols st) of-        Just ty -> return (ty, [])  -- Return the declared type without warning-        Nothing -> do-          permissive <- isPermissive-          if permissive-            then do-              -- In permissive mode, treat as a warning and return a fresh type variable-              addWarning $ UnboundVariableWarning name emptyContext-              t <- freshVar "unbound"-              return (t, [])-            else throwError $ UnboundVariable name emptyContext---- | Unify two types-unifyTypes :: Type -> Type -> Infer Subst-unifyTypes t1 t2 = unifyTypesWithContext t1 t2 emptyContext---- | Unify two types with context information--- This now uses the accumulated constraints from the Infer monad to properly--- handle constraint-aware unification (e.g., ensuring {Num a} a doesn't unify with Tensor b)-unifyTypesWithContext :: Type -> Type -> TypeErrorContext -> Infer Subst-unifyTypesWithContext t1 t2 ctx = do-  constraints <- getConstraints-  classEnv <- getClassEnv-  case TU.unifyWithConstraints classEnv constraints t1 t2 of-    Right (s, _)  -> return s  -- Discard flag in basic unification-    Left err -> case err of-      TU.OccursCheck v t -> throwError $ OccursCheckError v t ctx-      TU.TypeMismatch a b -> throwError $ UnificationError a b ctx---- | Unify two types with context, allowing Tensor a to unify with a--- This is used only for top-level definitions with type annotations--- According to type-tensor-simple.md: "Only for top-level tensor definitions, if Tensor a is unified with a, it becomes a."-unifyTypesWithTopLevel :: Type -> Type -> TypeErrorContext -> Infer Subst-unifyTypesWithTopLevel t1 t2 ctx = case TU.unifyWithTopLevel t1 t2 of-  Right s  -> return s-  Left err -> case err of-    TU.OccursCheck v t -> throwError $ OccursCheckError v t ctx-    TU.TypeMismatch a b -> throwError $ UnificationError a b ctx---- | Unify two types with constraint-aware handling--- This is crucial for unifying types when type variables have constraints--- (e.g., {Num t0}) - the constraint affects how Tensor types are unified-unifyTypesWithConstraints :: [Constraint] -> Type -> Type -> TypeErrorContext -> Infer Subst-unifyTypesWithConstraints constraints t1 t2 ctx = do-  classEnv <- getClassEnv-  case TU.unifyWithConstraints classEnv constraints t1 t2 of-    Right (s, _)  -> return s  -- Discard flag in basic unification-    Left err -> case err of-      TU.OccursCheck v t -> throwError $ OccursCheckError v t ctx-      TU.TypeMismatch a b -> throwError $ UnificationError a b ctx---- | Infer type for constants-inferConstant :: ConstantExpr -> Infer Type-inferConstant c = case c of-  CharExpr _    -> return TChar-  StringExpr _  -> return TString-  BoolExpr _    -> return TBool-  IntegerExpr _ -> return TInt-  FloatExpr _   -> return TFloat-  -- something : Matcher a (polymorphic matcher that matches any type)-  SomethingExpr -> do-    elemType <- freshVar "a"-    return (TMatcher elemType)-  -- undefined has a fresh type variable (bottom-like, can be any type)-  UndefinedExpr -> freshVar "undefined"------------------------------------------------------------------------------------- * Type Inference for IExpr------------------------------------------------------------------------------------- | Helper: Create a TIExpr with a simple monomorphic type (no type variables, no constraints)-mkTIExpr :: Type -> TIExprNode -> TIExpr-mkTIExpr ty node = TIExpr (Forall [] [] ty) node---- | Simplify Tensor constraints in type schemes--- Rewrites C (Tensor a) to C a when C (Tensor a) has no instance but C a does--- This enables correct type class expansion for higher-order functions with Tensor arguments-simplifyTensorConstraints :: ClassEnv -> [Constraint] -> [Constraint]-simplifyTensorConstraints classEnv = map simplifyConstraint-  where-    hasInstance :: String -> Type -> Bool-    hasInstance cls ty =-      case findMatchingInstanceForType ty (lookupInstances cls classEnv) of-        Just _  -> True-        Nothing -> False-    -    simplifyConstraint :: Constraint -> Constraint-    simplifyConstraint (Constraint cls ty) = Constraint cls (unwrapTensorInType cls ty)-      where-        unwrapTensorInType :: String -> Type -> Type-        unwrapTensorInType cls' ty0 = case ty0 of-          TTensor inner-            | hasInstance cls' ty0   -> ty0           -- Tensor has instance, keep it-            | hasInstance cls' inner -> unwrapTensorInType cls' inner  -- Unwrap recursively-            | otherwise              -> ty0           -- No instance for either, keep original-          _ -> ty0---- | Simplify Tensor constraints in a type scheme--- During type inference, keep type variables unquantified (Forall [])--- Quantification only happens at let/def boundaries-simplifyTensorConstraintsInScheme :: ClassEnv -> TypeScheme -> TypeScheme-simplifyTensorConstraintsInScheme classEnv (Forall tvs cs ty) =-  let cs' = simplifyTensorConstraints classEnv cs-  in Forall tvs cs' ty---- | Simplify Tensor constraints in a TIExpr-simplifyTensorConstraintsInTIExpr :: ClassEnv -> TIExpr -> TIExpr-simplifyTensorConstraintsInTIExpr classEnv (TIExpr scheme node) =-  TIExpr (simplifyTensorConstraintsInScheme classEnv scheme) node---- | Apply a substitution to a type scheme with class environment awareness--- This adjusts the substitution based on type class constraints:--- When {Num t0} t0 -> t0 is unified with Tensor t1, if Num (Tensor t1) has no instance,--- the substitution is adjusted to t0 -> t1 (unwrapping the Tensor)-applySubstSchemeWithClassEnv :: ClassEnv -> Subst -> TypeScheme -> TypeScheme-applySubstSchemeWithClassEnv classEnv (Subst m) (Forall vs cs t) =-  let m' = foldr Map.delete m vs-      -- Adjust substitution based on constraints-      m'' = adjustSubstForConstraints classEnv cs m'-      s' = Subst m''-  in Forall vs (map (applySubstConstraint s') cs) (applySubst s' t)-  where-    -- Adjust substitution to unwrap Tensor when constraint has no instance-    adjustSubstForConstraints :: ClassEnv -> [Constraint] -> Map.Map TyVar Type -> Map.Map TyVar Type-    adjustSubstForConstraints env constraints substMap =-      -- For each constraint, check if we need to adjust substitutions-      foldr (adjustForConstraint env substMap) substMap constraints--    adjustForConstraint :: ClassEnv -> Map.Map TyVar Type -> Constraint -> Map.Map TyVar Type -> Map.Map TyVar Type-    adjustForConstraint env originalSubst (Constraint cls constraintType) currentSubst =-      -- Get all type variables in the constraint type-      let constraintVars = Set.toList $ freeTyVars constraintType-      in foldr (adjustVarForClass env cls originalSubst) currentSubst constraintVars--    adjustVarForClass :: ClassEnv -> String -> Map.Map TyVar Type -> TyVar -> Map.Map TyVar Type -> Map.Map TyVar Type-    adjustVarForClass env cls originalSubst var currentSubst =-      case Map.lookup var originalSubst of-        Just replacementType@(TTensor _) ->-          -- This variable is being replaced with a Tensor type-          -- Check if the class has an instance for the Tensor type-          let instances = lookupInstances cls env-              hasTensorInstance = case findMatchingInstanceForType replacementType instances of-                                    Just _  -> True-                                    Nothing -> False-          in if hasTensorInstance-               then currentSubst  -- Keep the Tensor substitution-               else Map.insert var (unwrapTensorCompletely replacementType) currentSubst  -- Unwrap Tensor-        _ -> currentSubst  -- Not a Tensor substitution, keep as is--    -- Recursively unwrap Tensor to get the innermost type-    unwrapTensorCompletely :: Type -> Type-    unwrapTensorCompletely (TTensor inner) = unwrapTensorCompletely inner-    unwrapTensorCompletely ty = ty---- | Apply a substitution to a TIExpr, updating both the type scheme and all subexpressions-applySubstToTIExpr :: Subst -> TIExpr -> TIExpr-applySubstToTIExpr s (TIExpr scheme node) =-  let updatedScheme = applySubstScheme s scheme-      updatedNode = applySubstToTIExprNode s node-  in TIExpr updatedScheme updatedNode---- | Apply a substitution to a TIExpr with ClassEnv awareness--- This adjusts the substitution based on type class constraints--- Example: {Num t0} t0 -> t0 with substitution t0 -> Tensor t1---   If Num (Tensor t1) has no instance, the substitution is adjusted to t0 -> t1-applySubstToTIExprWithClassEnv :: ClassEnv -> Subst -> TIExpr -> TIExpr-applySubstToTIExprWithClassEnv classEnv s (TIExpr scheme node) =-  let updatedScheme = applySubstSchemeWithClassEnv classEnv s scheme-      updatedNode = applySubstToTIExprNodeWithClassEnv classEnv s node-  in TIExpr updatedScheme updatedNode---- | Monadic version that uses ClassEnv to adjust substitutions based on constraints--- Use this in type inference when you need to apply substitutions with constraint awareness-applySubstToTIExprM :: Subst -> TIExpr -> Infer TIExpr-applySubstToTIExprM s tiExpr = do-  classEnv <- getClassEnv-  return $ applySubstToTIExprWithClassEnv classEnv s tiExpr---- | Apply a substitution to a Type with constraint awareness--- This is a monadic version that retrieves ClassEnv and constraints from the Infer monad--- and adjusts the substitution based on type class constraints before applying it-applySubstWithConstraintsM :: Subst -> Type -> Infer Type-applySubstWithConstraintsM s@(Subst m) t = do-  classEnv <- getClassEnv-  constraints <- gets inferConstraints-  -- Adjust substitution based on constraints using the same logic as applySubstSchemeWithClassEnv-  let m' = adjustSubstForConstraints classEnv constraints m-      s' = Subst m'-  return $ applySubst s' t-  where-    -- Adjust substitution to unwrap Tensor when constraint has no instance-    adjustSubstForConstraints :: ClassEnv -> [Constraint] -> Map.Map TyVar Type -> Map.Map TyVar Type-    adjustSubstForConstraints env cs substMap =-      foldr (adjustForConstraint env substMap) substMap cs--    adjustForConstraint :: ClassEnv -> Map.Map TyVar Type -> Constraint -> Map.Map TyVar Type -> Map.Map TyVar Type-    adjustForConstraint env originalSubst (Constraint cls constraintType) currentSubst =-      let constraintVars = Set.toList $ freeTyVars constraintType-      in foldr (adjustVarForClass env cls originalSubst) currentSubst constraintVars--    adjustVarForClass :: ClassEnv -> String -> Map.Map TyVar Type -> TyVar -> Map.Map TyVar Type -> Map.Map TyVar Type-    adjustVarForClass env cls originalSubst var currentSubst =-      case Map.lookup var originalSubst of-        Just replacementType@(TTensor _) ->-          let instances = lookupInstances cls env-              hasTensorInstance = case findMatchingInstanceForType replacementType instances of-                                    Just _  -> True-                                    Nothing -> False-          in if hasTensorInstance-               then currentSubst-               else Map.insert var (unwrapTensorCompletely replacementType) currentSubst-        _ -> currentSubst--    unwrapTensorCompletely :: Type -> Type-    unwrapTensorCompletely (TTensor inner) = unwrapTensorCompletely inner-    unwrapTensorCompletely ty = ty---- | Apply a substitution to a TIExprNode recursively-applySubstToTIExprNode :: Subst -> TIExprNode -> TIExprNode-applySubstToTIExprNode s node = case node of-  TIConstantExpr c -> TIConstantExpr c-  TIVarExpr name -> TIVarExpr name-  -  TILambdaExpr mVar params body ->-    TILambdaExpr mVar params (applySubstToTIExpr s body)-  -  TIApplyExpr func args ->-    TIApplyExpr (applySubstToTIExpr s func) (map (applySubstToTIExpr s) args)-  -  TITupleExpr exprs ->-    TITupleExpr (map (applySubstToTIExpr s) exprs)-  -  TICollectionExpr exprs ->-    TICollectionExpr (map (applySubstToTIExpr s) exprs)-  -  TIConsExpr h t ->-    TIConsExpr (applySubstToTIExpr s h) (applySubstToTIExpr s t)-  -  TIJoinExpr l r ->-    TIJoinExpr (applySubstToTIExpr s l) (applySubstToTIExpr s r)-  -  TIIfExpr cond thenE elseE ->-    TIIfExpr (applySubstToTIExpr s cond) (applySubstToTIExpr s thenE) (applySubstToTIExpr s elseE)-  -  TILetExpr bindings body ->-    TILetExpr (map (\(pat, expr) -> (pat, applySubstToTIExpr s expr)) bindings)-              (applySubstToTIExpr s body)-  -  TILetRecExpr bindings body ->-    TILetRecExpr (map (\(pat, expr) -> (pat, applySubstToTIExpr s expr)) bindings)-                 (applySubstToTIExpr s body)-  -  TISeqExpr e1 e2 ->-    TISeqExpr (applySubstToTIExpr s e1) (applySubstToTIExpr s e2)-  -  TIInductiveDataExpr name exprs ->-    TIInductiveDataExpr name (map (applySubstToTIExpr s) exprs)-  -  TIMatcherExpr patDefs ->-    TIMatcherExpr (map (\(pat, expr, bindings) -> (pat, applySubstToTIExpr s expr, bindings)) patDefs)-  -  TIMatchExpr mode target matcher clauses ->-    TIMatchExpr mode -                (applySubstToTIExpr s target)-                (applySubstToTIExpr s matcher)-                (map (\(pat, body) -> (pat, applySubstToTIExpr s body)) clauses)-  -  TIMatchAllExpr mode target matcher clauses ->-    TIMatchAllExpr mode-                   (applySubstToTIExpr s target)-                   (applySubstToTIExpr s matcher)-                   (map (\(pat, body) -> (pat, applySubstToTIExpr s body)) clauses)-  -  TIMemoizedLambdaExpr params body ->-    TIMemoizedLambdaExpr params (applySubstToTIExpr s body)-  -  TIDoExpr bindings body ->-    TIDoExpr (map (\(pat, expr) -> (pat, applySubstToTIExpr s expr)) bindings)-             (applySubstToTIExpr s body)-  -  TICambdaExpr var body ->-    TICambdaExpr var (applySubstToTIExpr s body)-  -  TIWithSymbolsExpr syms body ->-    TIWithSymbolsExpr syms (applySubstToTIExpr s body)-  -  TIQuoteExpr e ->-    TIQuoteExpr (applySubstToTIExpr s e)-  -  TIQuoteSymbolExpr e ->-    TIQuoteSymbolExpr (applySubstToTIExpr s e)-  -  TIIndexedExpr override base indices ->-    TIIndexedExpr override (applySubstToTIExpr s base) (fmap (applySubstToTIExpr s) <$> indices)-  -  TISubrefsExpr override base ref ->-    TISubrefsExpr override (applySubstToTIExpr s base) (applySubstToTIExpr s ref)-  -  TISuprefsExpr override base ref ->-    TISuprefsExpr override (applySubstToTIExpr s base) (applySubstToTIExpr s ref)-  -  TIUserrefsExpr override base ref ->-    TIUserrefsExpr override (applySubstToTIExpr s base) (applySubstToTIExpr s ref)-  -  TIWedgeApplyExpr func args ->-    TIWedgeApplyExpr (applySubstToTIExpr s func) (map (applySubstToTIExpr s) args)-  -  TIFunctionExpr names ->-    TIFunctionExpr names-  -  TIVectorExpr exprs ->-    TIVectorExpr (map (applySubstToTIExpr s) exprs)-  -  TIHashExpr pairs ->-    TIHashExpr (map (\(k, v) -> (applySubstToTIExpr s k, applySubstToTIExpr s v)) pairs)-  -  TIGenerateTensorExpr func shape ->-    TIGenerateTensorExpr (applySubstToTIExpr s func) (applySubstToTIExpr s shape)-  -  TITensorExpr shape elems ->-    TITensorExpr (applySubstToTIExpr s shape) (applySubstToTIExpr s elems)-  -  TITransposeExpr perm tensor ->-    TITransposeExpr (applySubstToTIExpr s perm) (applySubstToTIExpr s tensor)-  -  TIFlipIndicesExpr tensor ->-    TIFlipIndicesExpr (applySubstToTIExpr s tensor)-  -  TITensorMapExpr func tensor ->-    TITensorMapExpr (applySubstToTIExpr s func) (applySubstToTIExpr s tensor)-  -  TITensorMap2Expr func t1 t2 ->-    TITensorMap2Expr (applySubstToTIExpr s func) (applySubstToTIExpr s t1) (applySubstToTIExpr s t2)-  -  TITensorContractExpr tensor ->-    TITensorContractExpr (applySubstToTIExpr s tensor)---- | Apply a substitution to a TIExprNode recursively with ClassEnv awareness-applySubstToTIExprNodeWithClassEnv :: ClassEnv -> Subst -> TIExprNode -> TIExprNode-applySubstToTIExprNodeWithClassEnv env s node = case node of-  TIConstantExpr c -> TIConstantExpr c-  TIVarExpr name -> TIVarExpr name--  TILambdaExpr mVar params body ->-    TILambdaExpr mVar params (applySubstToTIExprWithClassEnv env s body)--  TIApplyExpr func args ->-    TIApplyExpr (applySubstToTIExprWithClassEnv env s func) (map (applySubstToTIExprWithClassEnv env s) args)--  TITupleExpr exprs ->-    TITupleExpr (map (applySubstToTIExprWithClassEnv env s) exprs)--  TICollectionExpr exprs ->-    TICollectionExpr (map (applySubstToTIExprWithClassEnv env s) exprs)--  TIConsExpr h t ->-    TIConsExpr (applySubstToTIExprWithClassEnv env s h) (applySubstToTIExprWithClassEnv env s t)--  TIJoinExpr l r ->-    TIJoinExpr (applySubstToTIExprWithClassEnv env s l) (applySubstToTIExprWithClassEnv env s r)--  TIIfExpr cond thenE elseE ->-    TIIfExpr (applySubstToTIExprWithClassEnv env s cond) (applySubstToTIExprWithClassEnv env s thenE) (applySubstToTIExprWithClassEnv env s elseE)--  TILetExpr bindings body ->-    TILetExpr (map (\(pat, expr) -> (pat, applySubstToTIExprWithClassEnv env s expr)) bindings)-              (applySubstToTIExprWithClassEnv env s body)--  TILetRecExpr bindings body ->-    TILetRecExpr (map (\(pat, expr) -> (pat, applySubstToTIExprWithClassEnv env s expr)) bindings)-                 (applySubstToTIExprWithClassEnv env s body)--  TISeqExpr e1 e2 ->-    TISeqExpr (applySubstToTIExprWithClassEnv env s e1) (applySubstToTIExprWithClassEnv env s e2)--  TIInductiveDataExpr name exprs ->-    TIInductiveDataExpr name (map (applySubstToTIExprWithClassEnv env s) exprs)--  TIMatcherExpr patDefs ->-    TIMatcherExpr (map (\(pat, expr, bindings) -> (pat, applySubstToTIExprWithClassEnv env s expr, bindings)) patDefs)--  TIMatchExpr mode target matcher clauses ->-    TIMatchExpr mode-                (applySubstToTIExprWithClassEnv env s target)-                (applySubstToTIExprWithClassEnv env s matcher)-                (map (\(pat, body) -> (pat, applySubstToTIExprWithClassEnv env s body)) clauses)--  TIMatchAllExpr mode target matcher clauses ->-    TIMatchAllExpr mode-                   (applySubstToTIExprWithClassEnv env s target)-                   (applySubstToTIExprWithClassEnv env s matcher)-                   (map (\(pat, body) -> (pat, applySubstToTIExprWithClassEnv env s body)) clauses)--  TIMemoizedLambdaExpr params body ->-    TIMemoizedLambdaExpr params (applySubstToTIExprWithClassEnv env s body)--  TIDoExpr bindings body ->-    TIDoExpr (map (\(pat, expr) -> (pat, applySubstToTIExprWithClassEnv env s expr)) bindings)-             (applySubstToTIExprWithClassEnv env s body)--  TICambdaExpr var body ->-    TICambdaExpr var (applySubstToTIExprWithClassEnv env s body)--  TIWithSymbolsExpr syms body ->-    TIWithSymbolsExpr syms (applySubstToTIExprWithClassEnv env s body)--  TIQuoteExpr e ->-    TIQuoteExpr (applySubstToTIExprWithClassEnv env s e)--  TIQuoteSymbolExpr e ->-    TIQuoteSymbolExpr (applySubstToTIExprWithClassEnv env s e)--  TIIndexedExpr override base indices ->-    TIIndexedExpr override (applySubstToTIExprWithClassEnv env s base) (fmap (applySubstToTIExprWithClassEnv env s) <$> indices)--  TISubrefsExpr override base ref ->-    TISubrefsExpr override (applySubstToTIExprWithClassEnv env s base) (applySubstToTIExprWithClassEnv env s ref)--  TISuprefsExpr override base ref ->-    TISuprefsExpr override (applySubstToTIExprWithClassEnv env s base) (applySubstToTIExprWithClassEnv env s ref)--  TIUserrefsExpr override base ref ->-    TIUserrefsExpr override (applySubstToTIExprWithClassEnv env s base) (applySubstToTIExprWithClassEnv env s ref)--  TIWedgeApplyExpr func args ->-    TIWedgeApplyExpr (applySubstToTIExprWithClassEnv env s func) (map (applySubstToTIExprWithClassEnv env s) args)--  TIFunctionExpr names ->-    TIFunctionExpr names--  TIVectorExpr exprs ->-    TIVectorExpr (map (applySubstToTIExprWithClassEnv env s) exprs)--  TIHashExpr pairs ->-    TIHashExpr (map (\(k, v) -> (applySubstToTIExprWithClassEnv env s k, applySubstToTIExprWithClassEnv env s v)) pairs)--  TIGenerateTensorExpr func shape ->-    TIGenerateTensorExpr (applySubstToTIExprWithClassEnv env s func) (applySubstToTIExprWithClassEnv env s shape)--  TITensorExpr shape elems ->-    TITensorExpr (applySubstToTIExprWithClassEnv env s shape) (applySubstToTIExprWithClassEnv env s elems)--  TITransposeExpr perm tensor ->-    TITransposeExpr (applySubstToTIExprWithClassEnv env s perm) (applySubstToTIExprWithClassEnv env s tensor)--  TIFlipIndicesExpr tensor ->-    TIFlipIndicesExpr (applySubstToTIExprWithClassEnv env s tensor)--  TITensorMapExpr func tensor ->-    TITensorMapExpr (applySubstToTIExprWithClassEnv env s func) (applySubstToTIExprWithClassEnv env s tensor)--  TITensorMap2Expr func t1 t2 ->-    TITensorMap2Expr (applySubstToTIExprWithClassEnv env s func) (applySubstToTIExprWithClassEnv env s t1) (applySubstToTIExprWithClassEnv env s t2)--  TITensorContractExpr tensor ->-    TITensorContractExpr (applySubstToTIExprWithClassEnv env s tensor)---- | Infer type for IExpr--- NEW: Returns TIExpr (typed expression) instead of (IExpr, Type, Subst)--- This builds the recursive TIExpr structure directly during type inference-inferIExpr :: IExpr -> Infer (TIExpr, Subst)-inferIExpr expr = inferIExprWithContext expr emptyContext---- | Infer type for IExpr with context information--- NEW: Returns TIExpr (typed expression) with type information embedded-inferIExprWithContext :: IExpr -> TypeErrorContext -> Infer (TIExpr, Subst)-inferIExprWithContext expr ctx = case expr of-  -- Constants-  IConstantExpr c -> do-    ty <- inferConstant c-    let scheme = Forall [] [] ty-    return (TIExpr scheme (TIConstantExpr c), emptySubst)-  -  -- Variables-  IVarExpr name -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    -- Variables starting with ":::" are treated as Any type without warning-    if ":::" `isPrefixOf` name-      then do-        let scheme = Forall [] [] TAny-        return (TIExpr scheme (TIVarExpr name), emptySubst)-      else do-        (ty, constraints) <- lookupVarWithConstraints name-        let scheme = Forall [] constraints ty-        return (TIExpr scheme (TIVarExpr name), emptySubst)-  -  -- Tuples-  ITupleExpr elems -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    case elems of-      [] -> do-        -- Empty tuple: unit type ()-        let scheme = Forall [] [] (TTuple [])-        return (TIExpr scheme (TITupleExpr []), emptySubst)-      [single] -> do-        -- Single element tuple: same as the element itself (parentheses are just grouping)-        inferIExprWithContext single exprCtx-      _ -> do-        results <- mapM (\e -> inferIExprWithContext e exprCtx) elems-        let elemTIExprs = map fst results-            elemTypes = map (tiExprType . fst) results-            s = foldr composeSubst emptySubst (map snd results)-        -        -- Check if all elements are Matcher types-        -- If so, return Matcher (Tuple ...) instead of (Matcher ..., Matcher ...)-        appliedElemTypes <- mapM (applySubstWithConstraintsM s) elemTypes-        let matcherTypes = catMaybes (map extractMatcherType appliedElemTypes)-        -        if length matcherTypes == length appliedElemTypes && not (null appliedElemTypes)-          then do-            -- All elements are matchers: return Matcher (Tuple ...)-            let tupleType = TTuple matcherTypes-                resultType = TMatcher tupleType-                scheme = Forall [] [] resultType-            return (TIExpr scheme (TITupleExpr elemTIExprs), s)-          else do-            -- Not all elements are matchers: return regular tuple-            let resultType = TTuple appliedElemTypes-                scheme = Forall [] [] resultType-            return (TIExpr scheme (TITupleExpr elemTIExprs), s)-        where-          -- Extract the inner type from Matcher a -> Just a, otherwise Nothing-          extractMatcherType :: Type -> Maybe Type-          extractMatcherType (TMatcher t) = Just t-          extractMatcherType _ = Nothing-  -  -- Collections (Lists)-  ICollectionExpr elems -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    elemType <- freshVar "elem"-    (elemTIExprs, s) <- foldM (inferListElem elemType exprCtx) ([], emptySubst) elems-    elemType' <- applySubstWithConstraintsM s elemType-    let resultType = TCollection elemType'-    return (mkTIExpr resultType (TICollectionExpr (reverse elemTIExprs)), s)-    where-      inferListElem eType exprCtx (accExprs, s) e = do-        (tiExpr, s') <- inferIExprWithContext e exprCtx-        let t = tiExprType tiExpr-        eType' <- applySubstWithConstraintsM s eType-        s'' <- unifyTypesWithContext eType' t exprCtx-        return (tiExpr : accExprs, composeSubst s'' (composeSubst s' s))--  -- Cons-  IConsExpr headExpr tailExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (headTI, s1) <- inferIExprWithContext headExpr exprCtx-    (tailTI, s2) <- inferIExprWithContext tailExpr exprCtx-    let headType = tiExprType headTI-        tailType = tiExprType tailTI-        s12 = composeSubst s2 s1-    headType' <- applySubstWithConstraintsM s12 headType-    tailType' <- applySubstWithConstraintsM s12 tailType-    s3 <- unifyTypesWithContext (TCollection headType') tailType' exprCtx-    let finalS = composeSubst s3 s12-    resultType <- applySubstWithConstraintsM finalS tailType-    return (mkTIExpr resultType (TIConsExpr headTI tailTI), finalS)-  -  -- Join (list concatenation)-  IJoinExpr leftExpr rightExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (leftTI, s1) <- inferIExprWithContext leftExpr exprCtx-    (rightTI, s2) <- inferIExprWithContext rightExpr exprCtx-    let leftType = tiExprType leftTI-        rightType = tiExprType rightTI-        s12 = composeSubst s2 s1-    leftType' <- applySubstWithConstraintsM s12 leftType-    rightType' <- applySubstWithConstraintsM s12 rightType-    s3 <- unifyTypesWithContext leftType' rightType' exprCtx-    let finalS = composeSubst s3 s12-    resultType <- applySubstWithConstraintsM finalS leftType-    return (mkTIExpr resultType (TIJoinExpr leftTI rightTI), finalS)-  -  -- Hash (Map)-  IHashExpr pairs -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    keyType <- freshVar "hashKey"-    valType <- freshVar "hashVal"-    (pairTIs, s) <- foldM (inferHashPair keyType valType exprCtx) ([], emptySubst) pairs-    keyType' <- applySubstWithConstraintsM s keyType-    valType' <- applySubstWithConstraintsM s valType-    let resultType = THash keyType' valType'-    return (mkTIExpr resultType (TIHashExpr (reverse pairTIs)), s)-    where-      inferHashPair kType vType exprCtx (accPairs, s') (k, v) = do-        (kTI, s1) <- inferIExprWithContext k exprCtx-        (vTI, s2) <- inferIExprWithContext v exprCtx-        let kt = tiExprType kTI-            vt = tiExprType vTI-        kType' <- applySubstWithConstraintsM (composeSubst s2 s1) kType-        s3 <- unifyTypesWithContext kType' kt exprCtx-        vType' <- applySubstWithConstraintsM (composeSubst s3 (composeSubst s2 s1)) vType-        s4 <- unifyTypesWithContext vType' vt exprCtx-        return ((kTI, vTI) : accPairs, foldr composeSubst s' [s4, s3, s2, s1])-  -  -- Vector (Tensor)-  IVectorExpr elems -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    elemType <- freshVar "vecElem"-    (elemTIs, s) <- foldM (inferListElem elemType exprCtx) ([], emptySubst) elems-    elemType' <- applySubstWithConstraintsM s elemType-    let resultType = normalizeTensorType (TTensor elemType')-    return (mkTIExpr resultType (TIVectorExpr (reverse elemTIs)), s)-    where-      inferListElem eType exprCtx (accExprs, s) e = do-        (tiExpr, s') <- inferIExprWithContext e exprCtx-        let t = tiExprType tiExpr-        eType' <- applySubstWithConstraintsM s eType-        s'' <- unifyTypesWithContext eType' t exprCtx-        return (tiExpr : accExprs, composeSubst s'' (composeSubst s' s))--  -- Lambda-  ILambdaExpr mVar params body -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    argTypes <- mapM (\_ -> freshVar "arg") params-    let bindings = zipWith makeBinding params argTypes-    (bodyTIExpr, s) <- withEnv (map toScheme bindings) $ inferIExprWithContext body exprCtx-    let bodyType = tiExprType bodyTIExpr-    finalArgTypes <- mapM (applySubstWithConstraintsM s) argTypes-    let funType = foldr TFun bodyType finalArgTypes-    return (mkTIExpr funType (TILambdaExpr mVar params bodyTIExpr), s)-    where-      makeBinding var t = (extractNameFromVar var, t)-      toScheme (name, t) = (name, Forall [] [] t)-  -  -- Function Application-  IApplyExpr func args -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (funcTI, s1) <- inferIExprWithContext func exprCtx-    let funcType = tiExprType funcTI-    inferIApplicationWithContext funcTI funcType args s1 exprCtx--  -- Wedge apply expression (exterior product)-  IWedgeApplyExpr func args -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (funcTI, s1) <- inferIExprWithContext func exprCtx-    let funcType = tiExprType funcTI-    -- Wedge application is similar to normal application-    (resultTI, finalS) <- inferIApplicationWithContext funcTI funcType args s1 exprCtx-    -- Convert TIApplyExpr to TIWedgeApplyExpr to preserve wedge semantics-    let resultScheme = tiScheme resultTI-    case tiExprNode resultTI of-      TIApplyExpr funcTI' argTIs' ->-        return (TIExpr resultScheme (TIWedgeApplyExpr funcTI' argTIs'), finalS)-      _ -> return (resultTI, finalS)--  -- If expression-  IIfExpr cond thenExpr elseExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (condTI, s1) <- inferIExprWithContext cond exprCtx-    let condType = tiExprType condTI-    s2 <- unifyTypesWithContext condType TBool exprCtx-    let s12 = composeSubst s2 s1-    (thenTI, s3) <- inferIExprWithContext thenExpr exprCtx-    (elseTI, s4) <- inferIExprWithContext elseExpr exprCtx-    let thenType = tiExprType thenTI-        elseType = tiExprType elseTI-    thenType' <- applySubstWithConstraintsM s4 thenType-    s5 <- unifyTypesWithContext thenType' elseType exprCtx-    let finalS = foldr composeSubst emptySubst [s5, s4, s3, s12]-    resultType <- applySubstWithConstraintsM finalS elseType-    return (mkTIExpr resultType (TIIfExpr condTI thenTI elseTI), finalS)-  -  -- Let expression-  ILetExpr bindings body -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    env <- getEnv-    (bindingTIs, extendedEnv, s1) <- inferIBindingsWithContext bindings env emptySubst exprCtx-    (bodyTI, s2) <- withEnv extendedEnv $ inferIExprWithContext body exprCtx-    let bodyType = tiExprType bodyTI-        finalS = composeSubst s2 s1-    resultType <- applySubstWithConstraintsM finalS bodyType-    return (mkTIExpr resultType (TILetExpr bindingTIs bodyTI), finalS)-  -  -- LetRec expression-  ILetRecExpr bindings body -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    env <- getEnv-    (bindingTIs, extendedEnv, s1) <- inferIRecBindingsWithContext bindings env emptySubst exprCtx-    (bodyTI, s2) <- withEnv extendedEnv $ inferIExprWithContext body exprCtx-    let bodyType = tiExprType bodyTI-        finalS = composeSubst s2 s1-    resultType <- applySubstWithConstraintsM finalS bodyType-    return (mkTIExpr resultType (TILetRecExpr bindingTIs bodyTI), finalS)-  -  -- Sequence expression-  ISeqExpr expr1 expr2 -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (expr1TI, s1) <- inferIExprWithContext expr1 exprCtx-    (expr2TI, s2) <- inferIExprWithContext expr2 exprCtx-    let t2 = tiExprType expr2TI-    return (mkTIExpr t2 (TISeqExpr expr1TI expr2TI), composeSubst s2 s1)-  -  -- Inductive Data Constructor-  IInductiveDataExpr name args -> do-    -- Look up constructor type in environment-    env <- getEnv-    case lookupEnv (stringToVar name) env of-      Just scheme -> do-        -- Instantiate the type scheme-        st <- get-        let (_constraints, constructorType, newCounter) = instantiate scheme (inferCounter st)-        modify $ \s -> s { inferCounter = newCounter }-        -- Treat constructor as a function application-        inferIApplication name constructorType args emptySubst-      Nothing -> do-        -- Constructor not found in environment-        let exprCtx = withExpr (prettyStr expr) ctx-        permissive <- isPermissive-        if permissive-          then do-            -- In permissive mode, treat as a warning and return a fresh type variable-            addWarning $ UnboundVariableWarning name exprCtx-            resultType <- freshVar "ctor"-            return (mkTIExpr resultType (TIInductiveDataExpr name []), emptySubst)-          else throwError $ UnboundVariable name exprCtx-  -  -- Matchers (return Matcher type)-  IMatcherExpr patDefs -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    -- Infer type of each pattern definition (matcher clause)-    -- Each clause has: (PrimitivePatPattern, nextMatcherExpr, [(primitiveDataPat, targetExpr)])-    results <- mapM (inferPatternDef exprCtx) patDefs-    -    -- Collect TIPatternDefs and substitutions-    let tiPatDefs = map fst results-        substs = concatMap (snd . snd) results  -- Extract [Subst] from (TIPatternDef, (Type, [Subst]))-        finalSubst = foldr composeSubst emptySubst substs-    -    -- All clauses should agree on the matched type-    -- Unify all matched types from each pattern definition-    matchedTypes <- mapM (\(_, (ty, _)) -> applySubstWithConstraintsM finalSubst ty) results-    (matchedTy, s_matched) <- case matchedTypes of-      [] -> do-        ty <- freshVar "matched"-        return (ty, emptySubst)-      (firstTy:restTys) -> do-        -- Unify all matched types-        s <- foldM (\accS ty -> do-            firstTy' <- applySubstWithConstraintsM accS firstTy-            ty' <- applySubstWithConstraintsM accS ty-            s' <- unifyTypesWithContext firstTy' ty' exprCtx-            return $ composeSubst s' accS-          ) emptySubst restTys-        resultTy <- applySubstWithConstraintsM s firstTy-        return (resultTy, s)-    -    let allSubst = composeSubst s_matched finalSubst-    return (mkTIExpr (TMatcher matchedTy) (TIMatcherExpr tiPatDefs), allSubst)-    where-      -- Infer a single pattern definition (matcher clause)-      -- Returns (TIPatternDef, (matched type, [substitutions]))-      inferPatternDef :: TypeErrorContext -> IPatternDef -> Infer (TIPatternDef, (Type, [Subst]))-      inferPatternDef ctx (ppPat, nextMatcherExpr, dataClauses) = do-        -- Infer the type of next matcher expression-        -- It should be a Matcher type (possibly Matcher of tuple, like Matcher (a, b))-        -- Note: (integer, integer) is inferred as Matcher (Integer, Integer), not (Matcher Integer, Matcher Integer)-        (nextMatcherTI, s1) <- inferIExprWithContext nextMatcherExpr ctx-        let nextMatcherType = tiExprType nextMatcherTI-        -        -- nextMatcherType must be a Matcher type-        -- Unify with Matcher a to constrain it and detect errors early-        matcherInnerTy <- freshVar "matcherInner"-        nextMatcherType' <- applySubstWithConstraintsM s1 nextMatcherType-        s1' <- unifyTypesWithContext nextMatcherType' (TMatcher matcherInnerTy) ctx-        nextMatcherType'' <- applySubstWithConstraintsM s1' nextMatcherType-        -        -- Infer PrimitivePatPattern type to get matched type, pattern hole types, and variable bindings-        (matchedType, patternHoleTypes, ppBindings, s_pp) <- inferPrimitivePatPattern ppPat ctx-        let s1'' = composeSubst s_pp s1'-        matchedType' <- applySubstWithConstraintsM s1'' matchedType-        let -- Apply substitution to variable bindings-            ppBindings' = [(var, applySubstScheme s1'' scheme) | (var, scheme) <- ppBindings]--        -- Apply substitution to pattern hole types (keep as inner types)-        patternHoleTypes' <- mapM (applySubstWithConstraintsM s1'') patternHoleTypes--        -- Extract inner type(s) from next matcher type-        -- If multiple pattern holes, combine them into a tuple to match ITupleExpr behavior-        nextMatcherInnerTypes <- extractInnerTypesFromMatcher nextMatcherType'' (length patternHoleTypes') ctx-        -        -- Unify pattern hole types (inner types) with next matcher inner types-        s_unify <- checkPatternHoleConsistency patternHoleTypes' nextMatcherInnerTypes ctx-        let s1''' = composeSubst s_unify s1''-        -        -- Infer the type of data clauses with pp variables in scope-        -- Each data clause: (primitiveDataPattern, targetListExpr)-        dataClauseResults <- withEnv ppBindings' $ -          mapM (inferDataClauseWithCheck ctx nextMatcherInnerTypes matchedType') dataClauses-        let s2 = foldr composeSubst emptySubst dataClauseResults-        -        -- Build TIPatternDef: need to convert dataClauses to TIBindingExpr-        -- For each data clause, infer the pattern to get bindings, then infer the expression with those bindings-        dataClauseTIs <- withEnv ppBindings' $ -          mapM (\(pdPat, targetExpr) -> do-            -- Infer primitive data pattern to get variable bindings-            (_, pdBindings, _) <- inferPrimitiveDataPattern pdPat matchedType' ctx-            -- Infer target expression with both pp variables and pd pattern variables in scope-            (targetTI, _) <- withEnv pdBindings $ inferIExprWithContext targetExpr ctx-            return (pdPat, targetTI)) dataClauses-        -        let tiPatDef = (ppPat, nextMatcherTI, dataClauseTIs)-        -        return (tiPatDef, (matchedType', [s1''', s2]))-      -      -- Infer PrimitivePatPattern type-      -- Returns (matched type, pattern hole types, variable bindings, substitution)-      -- Pattern hole types are the inner types (without TMatcher wrapper)-      -- The caller should wrap them with TMatcher when unifying with next matcher types-      -- Variable bindings are for PPValuePat variables (#$val)-      -- Note: Pattern hole types are determined by the pattern constructor, not by external context-      inferPrimitivePatPattern :: PrimitivePatPattern -> TypeErrorContext -> Infer (Type, [Type], [(String, TypeScheme)], Subst)-      inferPrimitivePatPattern ppPat ctx = case ppPat of-        PPWildCard -> do-          -- Wildcard pattern: no pattern holes, no bindings-          matchedTy <- freshVar "matched"-          return (matchedTy, [], [], emptySubst)-        -        PPPatVar -> do-          -- Pattern variable ($): one pattern hole, no binding-          -- Returns the matched type as the pattern hole type-          -- The caller will wrap it with TMatcher when unifying with next matcher type-          matchedTy <- freshVar "matched"-          return (matchedTy, [matchedTy], [], emptySubst)-        -        PPValuePat var -> do-          -- Value pattern (#$val): no pattern holes, binds variable to matched type-          matchedTy <- freshVar "matched"-          let binding = (var, Forall [] [] matchedTy)-          return (matchedTy, [], [binding], emptySubst)-        -        PPTuplePat ppPats -> do-          -- Tuple pattern: ($p1, $p2, ...)-          -- Recursively infer each sub-pattern-          results <- mapM (\pp -> inferPrimitivePatPattern pp ctx) ppPats-          let matchedTypes = [mt | (mt, _, _, _) <- results]-              patternHoleLists = [phs | (_, phs, _, _) <- results]-              bindingLists = [bs | (_, _, bs, _) <- results]-              substs = [s | (_, _, _, s) <- results]-              allPatternHoles = concat patternHoleLists-              allBindings = concat bindingLists-              finalSubst = foldr composeSubst emptySubst substs-          -          -- Matched type is tuple of matched types-          matchedTypes' <- mapM (applySubstWithConstraintsM finalSubst) matchedTypes-          allPatternHoles' <- mapM (applySubstWithConstraintsM finalSubst) allPatternHoles-          let matchedTy = TTuple matchedTypes'-          return (matchedTy, allPatternHoles', allBindings, finalSubst)-        -        PPInductivePat name ppPats -> do-          -- Inductive pattern: look up pattern constructor type from pattern environment-          patternEnv <- getPatternEnv-          case lookupPatternEnv name patternEnv of-            Just scheme -> do-              -- Found in pattern environment: use the declared type-              st <- get-              let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)-              modify $ \s -> s { inferCounter = newCounter }-              -              -- Pattern constructor type: arg1 -> arg2 -> ... -> resultType-              -- Extract argument types and result type-              let (argTypes, resultType) = extractFunctionArgs ctorType-              -              -- Check argument count matches-              if length argTypes /= length ppPats-                then throwError $ TE.TypeMismatch-                       (foldr TFun resultType (replicate (length ppPats) (TVar (TyVar "a"))))-                       ctorType-                       ("Pattern constructor " ++ name ++ " expects " ++ show (length argTypes) -                        ++ " arguments, but got " ++ show (length ppPats))-                       ctx-                else do-                  -- Recursively infer each sub-pattern-                  results <- mapM (\pp -> inferPrimitivePatPattern pp ctx) ppPats-                  -                  let matchedTypes = [mt | (mt, _, _, _) <- results]-                      patternHoleLists = [phs | (_, phs, _, _) <- results]-                      bindingLists = [bs | (_, _, bs, _) <- results]-                      substs = [s | (_, _, _, s) <- results]-                      allPatternHoles = concat patternHoleLists-                      allBindings = concat bindingLists-                      s = foldr composeSubst emptySubst substs-                  -                  -- Verify that inferred matched types match expected argument types-                  -- Extract inner types from Matcher types in argTypes-                  let expectedMatchedTypes = map (\ty -> case ty of-                        TMatcher inner -> inner-                        _ -> ty) argTypes-                  s' <- foldM (\accS (inferredTy, expectedTy) -> do-                      inferredTy' <- applySubstWithConstraintsM accS inferredTy-                      expectedTy' <- applySubstWithConstraintsM accS expectedTy-                      s'' <- unifyTypesWithContext inferredTy' expectedTy' ctx-                      return $ composeSubst s'' accS-                    ) s (zip matchedTypes expectedMatchedTypes)--                  resultType' <- applySubstWithConstraintsM s' resultType-                  allPatternHoles' <- mapM (applySubstWithConstraintsM s') allPatternHoles-                  return (resultType', allPatternHoles', allBindings, s')-            -            Nothing -> do-              -- Not found in pattern environment: use generic inference-              -- This is for backward compatibility-              results <- mapM (\pp -> inferPrimitivePatPattern pp ctx) ppPats-              let matchedTypes = [mt | (mt, _, _, _) <- results]-                  patternHoleLists = [phs | (_, phs, _, _) <- results]-                  bindingLists = [bs | (_, _, bs, _) <- results]-                  substs = [s | (_, _, _, s) <- results]-                  allPatternHoles = concat patternHoleLists-                  allBindings = concat bindingLists-                  s = foldr composeSubst emptySubst substs-              -              -- Result type is inductive type-              matchedTypes' <- mapM (applySubstWithConstraintsM s) matchedTypes-              allPatternHoles' <- mapM (applySubstWithConstraintsM s) allPatternHoles-              let resultType = TInductive name matchedTypes'-              return (resultType, allPatternHoles', allBindings, s)-      -      -- Extract function argument types and result type-      -- e.g., a -> b -> c -> d  =>  ([a, b, c], d)-      extractFunctionArgs :: Type -> ([Type], Type)-      extractFunctionArgs (TFun arg rest) = -        let (args, result) = extractFunctionArgs rest-        in (arg : args, result)-      extractFunctionArgs t = ([], t)-      -      -- Extract matched type from Matcher type-      -- Check consistency between pattern hole types and next matcher types-      checkPatternHoleConsistency :: [Type] -> [Type] -> TypeErrorContext -> Infer Subst-      checkPatternHoleConsistency [] [] _ctx = return emptySubst-      checkPatternHoleConsistency patternHoles nextMatchers ctx-        | length patternHoles /= length nextMatchers = -            throwError $ TE.TypeMismatch-              (TTuple nextMatchers)-              (TTuple patternHoles)-              ("Inconsistent number of pattern holes (" ++ show (length patternHoles) -               ++ ") and next matchers (" ++ show (length nextMatchers) ++ ")")-              ctx-        | otherwise = do-            -- Unify each pattern hole type with corresponding next matcher type-            foldM (\accS (holeTy, matcherTy) -> do-                holeTy' <- applySubstWithConstraintsM accS holeTy-                matcherTy' <- applySubstWithConstraintsM accS matcherTy-                s <- unifyTypesWithContext holeTy' matcherTy' ctx-                return $ composeSubst s accS-              ) emptySubst (zip patternHoles nextMatchers)-      -      -- Extract inner types from next matcher type-      -- Given Matcher a, returns [a]-      -- Given Matcher (a, b, ...) and n pattern holes, returns [a, b, ...] if n > 1, or [(a, b, ...)] if n = 1-      -- Special case: (Matcher a, Matcher b, ...) should be converted to Matcher (a, b, ...) first-      -- Note: Even when numHoles = 0, we extract inner types to detect mismatches in checkPatternHoleConsistency-      extractInnerTypesFromMatcher :: Type -> Int -> TypeErrorContext -> Infer [Type]-      extractInnerTypesFromMatcher matcherType numHoles ctx = case numHoles of-        0 -> case matcherType of-          -- No pattern holes, but extract inner type to allow error detection-          TMatcher innerType -> return [innerType]-          TTuple types -> do-            let matcherInners = mapM extractMatcherInner types-            case matcherInners of-              Just inners -> return inners-              Nothing -> return []  -- Not matcher types, return empty-          _ -> return []  -- Not a matcher type-        1 -> case matcherType of-          TMatcher innerType -> return [innerType]  -- Single hole: return inner type as-is-          -- Special case: (Matcher a, Matcher b, ...) from ITupleExpr that failed to convert-          -- This can happen when matcher parameters are used before ITupleExpr conversion-          TTuple types -> do-            let matcherInners = mapM extractMatcherInner types-            case matcherInners of-              Just inners -> return [TTuple inners]  -- Return as single tuple type-              Nothing -> throwError $ TE.TypeMismatch-                           (TMatcher (TVar (TyVar "a")))-                           matcherType-                           "Expected Matcher type or tuple of Matcher types"-                           ctx-          _ -> throwError $ TE.TypeMismatch-                 (TMatcher (TVar (TyVar "a")))-                 matcherType-                 "Expected Matcher type"-                 ctx-        n -> case matcherType of-          -- Multiple holes: expect Matcher (tuple) and extract each element-          TMatcher (TTuple innerTypes) ->-            if length innerTypes == n-              then return innerTypes-              else throwError $ TE.TypeMismatch-                     (TMatcher (TTuple (replicate n (TVar (TyVar "a")))))-                     matcherType-                     ("Expected Matcher with tuple of " ++ show n ++ " elements, but got " ++ show (length innerTypes))-                     ctx-          -- Special case: (Matcher a, Matcher b, ...) - extract inner types directly-          TTuple types -> do-            let matcherInners = mapM extractMatcherInner types-            case matcherInners of-              Just inners | length inners == n -> return inners-              _ -> throwError $ TE.TypeMismatch-                     (TMatcher (TTuple (replicate n (TVar (TyVar "a")))))-                     matcherType-                     "Expected tuple of Matcher types with correct count"-                     ctx-          _ -> throwError $ TE.TypeMismatch-                 (TMatcher (TTuple (replicate n (TVar (TyVar "a")))))-                 matcherType-                 ("Expected Matcher of tuple with " ++ show n ++ " elements")-                 ctx-      -      -- Helper: Extract inner type from Matcher a -> Just a, otherwise Nothing-      extractMatcherInner :: Type -> Maybe Type-      extractMatcherInner (TMatcher t) = Just t-      extractMatcherInner _ = Nothing-      -      -- Infer a data clause with type checking-      -- Check that the target expression returns a list of values with types matching next matcher inner types-      -- Also uses matched type for validation-      -- nextMatcherInnerTypes: inner types extracted from next matcher (already without TMatcher wrapper)-      inferDataClauseWithCheck :: TypeErrorContext -> [Type] -> Type -> (IPrimitiveDataPattern, IExpr) -> Infer Subst-      inferDataClauseWithCheck ctx nextMatcherInnerTypes matchedType (pdPat, targetExpr) = do-        -- Extract expected element type from next matcher inner types (the target type)-        -- This is the type of elements in the list returned by the target expression-        targetType <- case nextMatcherInnerTypes of-          [] -> return (TTuple [])  -- No pattern holes: empty tuple () case-          [single] -> return single  -- Single pattern hole: use inner type directly-          multiple -> return (TTuple multiple)  -- Multiple holes: tuple of inner types-        -        -- Infer PrimitiveDataPattern with matched type-        -- Primitive data pattern matches against values of the matched type-        -- and produces bindings and next targets-        (pdTargetType, bindings, s_pd) <- inferPrimitiveDataPattern pdPat matchedType ctx-        -        -- The primitive data pattern should match the matched type-        -- No need to unify pdTargetType with targetType - they serve different purposes-        -- pdTargetType: type of data that pdPat matches (should be matchedType)-        -- targetType: type of next targets returned by the target expression-        -        -- Verify that pdTargetType is consistent with matchedType-        pdTargetType' <- applySubstWithConstraintsM s_pd pdTargetType-        matchedType' <- applySubstWithConstraintsM s_pd matchedType-        s_match <- unifyTypesWithContext pdTargetType' matchedType' ctx-        let s_pd' = composeSubst s_match s_pd--        -- Infer the target expression with pattern variables in scope-        (targetTI, s1) <- withEnv bindings $ inferIExprWithContext targetExpr ctx-        let exprType = tiExprType targetTI-            s_combined = composeSubst s1 s_pd'--        -- Unify with actual expression type-        -- Expected: [targetType]-        targetType' <- applySubstWithConstraintsM s_combined targetType-        let expectedType = TCollection targetType'--        exprType' <- applySubstWithConstraintsM s_combined exprType-        s2 <- unifyTypesWithContext exprType' expectedType ctx-        return $ composeSubst s2 s_combined-      -      -- Helper to check if a pattern is a pattern variable-      isPDPatVar :: IPrimitiveDataPattern -> Bool-      isPDPatVar (PDPatVar _) = True-      isPDPatVar _ = False-      -      -- Infer PrimitiveDataPattern type-      -- Returns (inferred target type, variable bindings, substitution)-      -- This is similar to pattern matching in Haskell for algebraic data types-      inferPrimitiveDataPattern :: IPrimitiveDataPattern -> Type -> TypeErrorContext -> Infer (Type, [(String, TypeScheme)], Subst)-      inferPrimitiveDataPattern pdPat expectedType ctx = case pdPat of-        PDWildCard -> do-          -- Wildcard: matches any type, no bindings-          return (expectedType, [], emptySubst)-        -        PDPatVar var -> do-          -- Pattern variable: binds to the expected type-          let varName = extractNameFromVar var-          return (expectedType, [(varName, Forall [] [] expectedType)], emptySubst)-        -        PDConstantPat c -> do-          -- Constant pattern: must match the constant's type-          constTy <- inferConstant c-          s <- unifyTypesWithContext constTy expectedType ctx-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', [], s)-        -        PDTuplePat pats -> do-          -- Tuple pattern: expected type should be a tuple-          case expectedType of-            TTuple types | length types == length pats -> do-              -- Types match: infer each sub-pattern-              results <- zipWithM (\p t -> inferPrimitiveDataPattern p t ctx) pats types-              let (_, bindingsList, substs) = unzip3 results-                  allBindings = concat bindingsList-                  s = foldr composeSubst emptySubst substs-              expectedType' <- applySubstWithConstraintsM s expectedType-              return (expectedType', allBindings, s)-            -            TVar _ -> do-              -- Expected type is a type variable: create fresh types for each element-              elemTypes <- mapM (\_ -> freshVar "elem") pats-              let tupleTy = TTuple elemTypes-              s <- unifyTypesWithContext expectedType tupleTy ctx--              -- Recursively infer each sub-pattern-              elemTypes' <- mapM (applySubstWithConstraintsM s) elemTypes-              results <- zipWithM (\p t -> inferPrimitiveDataPattern p t ctx) pats elemTypes'-              let (_, bindingsList, substs) = unzip3 results-                  allBindings = concat bindingsList-                  s' = foldr composeSubst s substs-              tupleTy' <- applySubstWithConstraintsM s' tupleTy-              return (tupleTy', allBindings, s')-            -            _ -> do-              -- Type mismatch-              throwError $ TE.TypeMismatch-                (TTuple (replicate (length pats) (TVar (TyVar "a"))))-                expectedType-                "Tuple pattern but target is not a tuple type"-                ctx-        -        PDEmptyPat -> do-          -- Empty collection pattern: expected type should be [a] for some a-          elemTy <- freshVar "elem"-          s <- unifyTypesWithContext expectedType (TCollection elemTy) ctx-          collTy <- applySubstWithConstraintsM s (TCollection elemTy)-          return (collTy, [], s)-        -        PDConsPat p1 p2 -> do-          -- Cons pattern: expected type should be [a] for some a-          case expectedType of-            TCollection elemType -> do-              -- Infer head pattern with element type-              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 elemType ctx-              -- Infer tail pattern with collection type-              expectedType' <- applySubstWithConstraintsM s1 expectedType-              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 expectedType' ctx-              let s = composeSubst s2 s1-              expectedType'' <- applySubstWithConstraintsM s expectedType-              return (expectedType'', bindings1 ++ bindings2, s)-            -            TVar _ -> do-              -- Expected type is a type variable: constrain it to be a collection-              elemTy <- freshVar "elem"-              s <- unifyTypesWithContext expectedType (TCollection elemTy) ctx-              collTy <- applySubstWithConstraintsM s (TCollection elemTy)-              elemTy' <- applySubstWithConstraintsM s elemTy-              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 elemTy' ctx-              collTy' <- applySubstWithConstraintsM s1 collTy-              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 collTy' ctx-              let s' = composeSubst s2 (composeSubst s1 s)-              collTy'' <- applySubstWithConstraintsM s' collTy-              return (collTy'', bindings1 ++ bindings2, s')-            -            _ -> do-              throwError $ TE.TypeMismatch-                (TCollection (TVar (TyVar "a")))-                expectedType-                "Cons pattern but target is not a collection type"-                ctx-        -        PDSnocPat p1 p2 -> do-          -- Snoc pattern: similar to cons but reversed-          case expectedType of-            TCollection elemType -> do-              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 expectedType ctx-              elemType' <- applySubstWithConstraintsM s1 elemType-              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 elemType' ctx-              let s = composeSubst s2 s1-              expectedType' <- applySubstWithConstraintsM s expectedType-              return (expectedType', bindings1 ++ bindings2, s)-            -            TVar _ -> do-              elemTy <- freshVar "elem"-              s <- unifyTypesWithContext expectedType (TCollection elemTy) ctx-              collTy <- applySubstWithConstraintsM s (TCollection elemTy)-              elemTy' <- applySubstWithConstraintsM s elemTy-              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 collTy ctx-              elemTy'' <- applySubstWithConstraintsM s1 elemTy'-              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 elemTy'' ctx-              let s' = composeSubst s2 (composeSubst s1 s)-              collTy' <- applySubstWithConstraintsM s' collTy-              return (collTy', bindings1 ++ bindings2, s')-            -            _ -> do-              throwError $ TE.TypeMismatch-                (TCollection (TVar (TyVar "a")))-                expectedType-                "Snoc pattern but target is not a collection type"-                ctx-        -        PDInductivePat name pats -> do-          -- Inductive pattern: look up data constructor type from environment-          env <- getEnv-          case lookupEnv (stringToVar name) env of-            Just scheme -> do-              -- Found in environment: use the declared type-              st <- get-              let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)-              modify $ \s -> s { inferCounter = newCounter }-              -              -- Data constructor type: arg1 -> arg2 -> ... -> resultType-              let (argTypes, resultType) = extractFunctionArgs ctorType-              -              -- Check argument count matches-              if length argTypes /= length pats-                then throwError $ TE.TypeMismatch-                       (foldr TFun resultType (replicate (length pats) (TVar (TyVar "a"))))-                       ctorType-                       ("Data constructor " ++ name ++ " expects " ++ show (length argTypes) -                        ++ " arguments, but got " ++ show (length pats))-                       ctx-                else do-                  -- Unify result type with expected type-                  s0 <- unifyTypesWithContext resultType expectedType ctx-                  resultType' <- applySubstWithConstraintsM s0 resultType-                  argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes--                  -- Recursively infer each sub-pattern-                  results <- zipWithM (\p argTy -> inferPrimitiveDataPattern p argTy ctx) pats argTypes'-                  let (_, bindingsList, substs) = unzip3 results-                      allBindings = concat bindingsList-                      s = foldr composeSubst s0 substs--                  -- Return the result type, not expected type-                  resultType'' <- applySubstWithConstraintsM s resultType'-                  return (resultType'', allBindings, s)-            -            Nothing -> do-              -- Not found in environment: use generic inference-              argTypes <- mapM (\_ -> freshVar "arg") pats-              let resultType = TInductive name argTypes--              s0 <- unifyTypesWithContext resultType expectedType ctx-              resultType' <- applySubstWithConstraintsM s0 resultType--              argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes-              results <- zipWithM (\p argTy -> inferPrimitiveDataPattern p argTy ctx) pats argTypes'-              let (_, bindingsList, substs) = unzip3 results-                  allBindings = concat bindingsList-                  s = foldr composeSubst s0 substs--              resultType'' <- applySubstWithConstraintsM s resultType'-              return (resultType'', allBindings, s)-        -        -- ScalarData (MathExpr) primitive patterns-        PDDivPat patNum patDen -> do-          -- Div: MathExpr -> PolyExpr, PolyExpr-          -- However, if pattern is a pattern variable, it gets MathExpr (auto-conversion)-          let polyExprTy = TPolyExpr-              mathExprTy = TMathExpr-              numTy = if isPDPatVar patNum then mathExprTy else polyExprTy-              denTy = if isPDPatVar patDen then mathExprTy else polyExprTy-          (_, bindings1, s1) <- inferPrimitiveDataPattern patNum numTy ctx-          denTy' <- applySubstWithConstraintsM s1 denTy-          (_, bindings2, s2) <- inferPrimitiveDataPattern patDen denTy' ctx-          let s = composeSubst s2 s1-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings1 ++ bindings2, s)-        -        PDPlusPat patTerms -> do-          -- Plus: PolyExpr -> [TermExpr]-          -- If pattern variable, it gets [MathExpr]-          let termExprTy = TTermExpr-              mathExprTy = TMathExpr-              termsTy = if isPDPatVar patTerms then TCollection mathExprTy else TCollection termExprTy-          (_, bindings, s) <- inferPrimitiveDataPattern patTerms termsTy ctx-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings, s)-        -        PDTermPat patCoeff patMonomials -> do-          -- Term: TermExpr -> Integer, [(SymbolExpr, Integer)]-          -- If patMonomials is pattern variable, it gets [(MathExpr, Integer)]-          let symbolExprTy = TSymbolExpr-              mathExprTy = TMathExpr-              monomialsElemTy = if isPDPatVar patMonomials-                                then TTuple [mathExprTy, TInt]-                                else TTuple [symbolExprTy, TInt]-          (_, bindings1, s1) <- inferPrimitiveDataPattern patCoeff TInt ctx-          monomialsCollTy <- applySubstWithConstraintsM s1 (TCollection monomialsElemTy)-          (_, bindings2, s2) <- inferPrimitiveDataPattern patMonomials monomialsCollTy ctx-          let s = composeSubst s2 s1-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings1 ++ bindings2, s)-        -        PDSymbolPat patName patIndices -> do-          -- Symbol: SymbolExpr -> String, [IndexExpr]-          -- patName and patIndices types don't change for pattern variables-          let indexExprTy = TIndexExpr-          (_, bindings1, s1) <- inferPrimitiveDataPattern patName TString ctx-          indicesCollTy <- applySubstWithConstraintsM s1 (TCollection indexExprTy)-          (_, bindings2, s2) <- inferPrimitiveDataPattern patIndices indicesCollTy ctx-          let s = composeSubst s2 s1-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings1 ++ bindings2, s)-        -        PDApply1Pat patFn patArg -> do-          -- Apply1: SymbolExpr -> (MathExpr -> MathExpr), MathExpr-          let mathExprTy = TMathExpr-              fnTy = TFun mathExprTy mathExprTy-          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx-          mathExprTy' <- applySubstWithConstraintsM s1 mathExprTy-          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg mathExprTy' ctx-          let s = composeSubst s2 s1-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings1 ++ bindings2, s)-        -        PDApply2Pat patFn patArg1 patArg2 -> do-          let mathExprTy = TMathExpr-              fnTy = TFun mathExprTy (TFun mathExprTy mathExprTy)-          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx-          mathExprTy1 <- applySubstWithConstraintsM s1 mathExprTy-          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg1 mathExprTy1 ctx-          mathExprTy2 <- applySubstWithConstraintsM s2 mathExprTy-          (_, bindings3, s3) <- inferPrimitiveDataPattern patArg2 mathExprTy2 ctx-          let s = composeSubst s3 (composeSubst s2 s1)-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings1 ++ bindings2 ++ bindings3, s)-        -        PDApply3Pat patFn patArg1 patArg2 patArg3 -> do-          let mathExprTy = TMathExpr-              fnTy = TFun mathExprTy (TFun mathExprTy (TFun mathExprTy mathExprTy))-          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx-          mathExprTy1 <- applySubstWithConstraintsM s1 mathExprTy-          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg1 mathExprTy1 ctx-          mathExprTy2 <- applySubstWithConstraintsM s2 mathExprTy-          (_, bindings3, s3) <- inferPrimitiveDataPattern patArg2 mathExprTy2 ctx-          mathExprTy3 <- applySubstWithConstraintsM s3 mathExprTy-          (_, bindings4, s4) <- inferPrimitiveDataPattern patArg3 mathExprTy3 ctx-          let s = composeSubst s4 (composeSubst s3 (composeSubst s2 s1))-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings1 ++ bindings2 ++ bindings3 ++ bindings4, s)-        -        PDApply4Pat patFn patArg1 patArg2 patArg3 patArg4 -> do-          let mathExprTy = TMathExpr-              fnTy = TFun mathExprTy (TFun mathExprTy (TFun mathExprTy (TFun mathExprTy mathExprTy)))-          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx-          mathExprTy1 <- applySubstWithConstraintsM s1 mathExprTy-          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg1 mathExprTy1 ctx-          mathExprTy2 <- applySubstWithConstraintsM s2 mathExprTy-          (_, bindings3, s3) <- inferPrimitiveDataPattern patArg2 mathExprTy2 ctx-          mathExprTy3 <- applySubstWithConstraintsM s3 mathExprTy-          (_, bindings4, s4) <- inferPrimitiveDataPattern patArg3 mathExprTy3 ctx-          mathExprTy4 <- applySubstWithConstraintsM s4 mathExprTy-          (_, bindings5, s5) <- inferPrimitiveDataPattern patArg4 mathExprTy4 ctx-          let s = composeSubst s5 (composeSubst s4 (composeSubst s3 (composeSubst s2 s1)))-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings1 ++ bindings2 ++ bindings3 ++ bindings4 ++ bindings5, s)-        -        PDQuotePat patExpr -> do-          -- Quote: SymbolExpr -> MathExpr-          let mathExprTy = TMathExpr-          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathExprTy ctx-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings, s)-        -        PDFunctionPat patName patArgs -> do-          -- Function: SymbolExpr -> MathExpr, [MathExpr]-          let mathExprTy = TMathExpr-          (_, bindings1, s1) <- inferPrimitiveDataPattern patName mathExprTy ctx-          argsCollTy <- applySubstWithConstraintsM s1 (TCollection mathExprTy)-          (_, bindings2, s2) <- inferPrimitiveDataPattern patArgs argsCollTy ctx-          expectedType' <- applySubstWithConstraintsM s2 expectedType-          return (expectedType', bindings1 ++ bindings2, s2)-        -        PDSubPat patExpr -> do-          -- Sub: IndexExpr -> MathExpr-          let mathExprTy = TMathExpr-          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathExprTy ctx-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings, s)--        PDSupPat patExpr -> do-          -- Sup: IndexExpr -> MathExpr-          let mathExprTy = TMathExpr-          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathExprTy ctx-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings, s)-        -        PDUserPat patExpr -> do-          -- User: IndexExpr -> MathExpr-          let mathExprTy = TMathExpr-          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathExprTy ctx-          expectedType' <- applySubstWithConstraintsM s expectedType-          return (expectedType', bindings, s)-  -  -- Match expressions (pattern matching)-  IMatchExpr mode target matcher clauses -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (targetTI, s1) <- inferIExprWithContext target exprCtx-    (matcherTI, s2) <- inferIExprWithContext matcher exprCtx-    let targetType = tiExprType targetTI-        matcherType = tiExprType matcherTI--    -- Matcher should be TMatcher a or (TMatcher a, TMatcher b, ...) which becomes TMatcher (a, b, ...)-    let s12 = composeSubst s2 s1-    appliedMatcherType <- applySubstWithConstraintsM s12 matcherType--    -- Normalize matcher type: if it's a tuple, ensure each element is a Matcher-    (_normalizedMatcherType, matchedInnerType, s3) <- case appliedMatcherType of-      TTuple elemTypes -> do-        -- Each tuple element should be Matcher ai-        matchedInnerTypes <- mapM (\_ -> freshVar "matched") elemTypes-        s_elems <- foldM (\accS (elemTy, innerTy) -> do-          appliedElemTy <- applySubstWithConstraintsM accS elemTy-          appliedInnerTy <- applySubstWithConstraintsM accS innerTy-          s' <- unifyTypesWithContext appliedElemTy (TMatcher appliedInnerTy) exprCtx-          return $ composeSubst s' accS-          ) emptySubst (zip elemTypes matchedInnerTypes)-        -- The tuple as a whole becomes Matcher (a1, a2, ...)-        finalInnerTypes <- mapM (applySubstWithConstraintsM s_elems) matchedInnerTypes-        let tupleInnerType = TTuple finalInnerTypes-        return (TMatcher tupleInnerType, tupleInnerType, s_elems)-      _ -> do-        -- Single matcher: TMatcher a-        matchedTy <- freshVar "matched"-        s' <- unifyTypesWithContext appliedMatcherType (TMatcher matchedTy) exprCtx-        finalMatchedTy <- applySubstWithConstraintsM s' matchedTy-        return (TMatcher finalMatchedTy, finalMatchedTy, s')--    let s123 = composeSubst s3 s12-    targetType' <- applySubstWithConstraintsM s123 targetType-    matchedInnerType' <- applySubstWithConstraintsM s123 matchedInnerType-    s4 <- unifyTypesWithContext targetType' matchedInnerType' exprCtx-    -    -- Infer match clauses result type-    let s1234 = composeSubst s4 s123-    case clauses of-      [] -> do-        -- No clauses: this should not happen, but handle gracefully-        resultTy <- freshVar "matchResult"-        targetTI' <- applySubstToTIExprM s1234 targetTI-        matcherTI' <- applySubstToTIExprM s1234 matcherTI-        resultTy' <- applySubstWithConstraintsM s1234 resultTy-        return (mkTIExpr resultTy' (TIMatchExpr mode targetTI' matcherTI' []), s1234)-      _ -> do-        -- Infer type of each clause and unify them-        matchedInnerType' <- applySubstWithConstraintsM s1234 matchedInnerType-        (resultTy, clauseTIs, clauseSubst) <- inferMatchClauses exprCtx matchedInnerType' clauses s1234-        let finalS = composeSubst clauseSubst s1234-        targetTI' <- applySubstToTIExprM finalS targetTI-        matcherTI' <- applySubstToTIExprM finalS matcherTI-        resultTy' <- applySubstWithConstraintsM finalS resultTy-        return (mkTIExpr resultTy' (TIMatchExpr mode targetTI' matcherTI' clauseTIs), finalS)-  -  -- MatchAll expressions-  IMatchAllExpr mode target matcher clauses -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (targetTI, s1) <- inferIExprWithContext target exprCtx-    (matcherTI, s2) <- inferIExprWithContext matcher exprCtx-    let targetType = tiExprType targetTI-        matcherType = tiExprType matcherTI-    -    -- Matcher should be TMatcher a or (TMatcher a, TMatcher b, ...) which becomes TMatcher (a, b, ...)-    let s12 = composeSubst s2 s1-    appliedMatcherType <- applySubstWithConstraintsM s12 matcherType-    -    -- Normalize matcher type: if it's a tuple, ensure each element is a Matcher-    (_normalizedMatcherType, matchedInnerType, s3) <- case appliedMatcherType of-      TTuple elemTypes -> do-        -- Each tuple element should be Matcher ai-        matchedInnerTypes <- mapM (\_ -> freshVar "matched") elemTypes-        s_elems <- foldM (\accS (elemTy, innerTy) -> do-          appliedElemTy <- applySubstWithConstraintsM accS elemTy-          appliedInnerTy <- applySubstWithConstraintsM accS innerTy-          s' <- unifyTypesWithContext appliedElemTy (TMatcher appliedInnerTy) exprCtx-          return $ composeSubst s' accS-          ) emptySubst (zip elemTypes matchedInnerTypes)-        -- The tuple as a whole becomes Matcher (a1, a2, ...)-        finalInnerTypes <- mapM (applySubstWithConstraintsM s_elems) matchedInnerTypes-        let tupleInnerType = TTuple finalInnerTypes-        return (TMatcher tupleInnerType, tupleInnerType, s_elems)-      _ -> do-        -- Single matcher: TMatcher a-        matchedTy <- freshVar "matched"-        s' <- unifyTypesWithContext appliedMatcherType (TMatcher matchedTy) exprCtx-        finalMatchedTy <- applySubstWithConstraintsM s' matchedTy-        return (TMatcher finalMatchedTy, finalMatchedTy, s')--    let s123 = composeSubst s3 s12-    targetType' <- applySubstWithConstraintsM s123 targetType-    matchedInnerType' <- applySubstWithConstraintsM s123 matchedInnerType-    s4 <- unifyTypesWithContext targetType' matchedInnerType' exprCtx-    -    -- MatchAll returns a collection of results from match clauses-    let s1234 = composeSubst s4 s123-    case clauses of-      [] -> do-        -- No clauses: return empty collection type-        resultElemTy <- freshVar "matchAllElem"-        targetTI' <- applySubstToTIExprM s1234 targetTI-        matcherTI' <- applySubstToTIExprM s1234 matcherTI-        resultElemTy' <- applySubstWithConstraintsM s1234 resultElemTy-        return (mkTIExpr (TCollection resultElemTy') (TIMatchAllExpr mode targetTI' matcherTI' []), s1234)-      _ -> do-        -- Infer type of each clause (they should all have the same type)-        matchedInnerType' <- applySubstWithConstraintsM s1234 matchedInnerType-        (resultElemTy, clauseTIs, clauseSubst) <- inferMatchClauses exprCtx matchedInnerType' clauses s1234-        let finalS = composeSubst clauseSubst s1234-        targetTI' <- applySubstToTIExprM finalS targetTI-        matcherTI' <- applySubstToTIExprM finalS matcherTI-        resultElemTy' <- applySubstWithConstraintsM finalS resultElemTy-        return (mkTIExpr (TCollection resultElemTy') (TIMatchAllExpr mode targetTI' matcherTI' clauseTIs), finalS)-  -  -- Memoized Lambda-  IMemoizedLambdaExpr args body -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    argTypes <- mapM (\_ -> freshVar "memoArg") args-    let bindings = zip args argTypes  -- [(String, Type)]-        schemes = map (\(name, t) -> (name, Forall [] [] t)) bindings-    (bodyTI, s) <- withEnv schemes $ inferIExprWithContext body exprCtx-    let bodyType = tiExprType bodyTI-    finalArgTypes <- mapM (applySubstWithConstraintsM s) argTypes-    let funType = foldr TFun bodyType finalArgTypes-    return (mkTIExpr funType (TIMemoizedLambdaExpr args bodyTI), s)-  -  -- Do expression-  IDoExpr bindings body -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    -- Infer IO monad bindings: each binding should be of type IO a-    env <- getEnv-    (bindingTIs, bindingSchemes, s1) <- inferIOBindingsWithContext bindings env emptySubst exprCtx-    (bodyTI, s2) <- withEnv bindingSchemes $ inferIExprWithContext body exprCtx-    let bodyType = tiExprType bodyTI-        finalS = composeSubst s2 s1-        -    -- Verify that body type is IO a-    bodyResultType <- freshVar "ioResult"-    bodyType' <- applySubstWithConstraintsM finalS bodyType-    s3 <- unifyTypesWithContext bodyType' (TIO bodyResultType) exprCtx-    resultType <- applySubstWithConstraintsM s3 (TIO bodyResultType)-    let finalS' = composeSubst s3 finalS-    return (mkTIExpr resultType (TIDoExpr bindingTIs bodyTI), finalS')-  -  -- Cambda (pattern matching lambda)-  ICambdaExpr var body -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    argType <- freshVar "cambdaArg"-    (bodyTI, s) <- inferIExprWithContext body exprCtx-    let bodyType = tiExprType bodyTI-    return (mkTIExpr (TFun argType bodyType) (TICambdaExpr var bodyTI), s)-  -  -- With symbols-  IWithSymbolsExpr syms body -> do-    -- Add symbols to type environment as MathExpr (TMathExpr = TInt)-    -- Symbols introduced by withSymbols are mathematical symbols-    let symbolBindings = [(sym, Forall [] [] TMathExpr) | sym <- syms]-    (bodyTI, s) <- withEnv symbolBindings $ inferIExprWithContext body ctx-    let bodyType = tiExprType bodyTI-    return (mkTIExpr bodyType (TIWithSymbolsExpr syms bodyTI), s)-  -  -- Quote expressions (symbolic math)-  IQuoteExpr e -> do-    (eTI, s) <- inferIExprWithContext e ctx-    return (mkTIExpr TInt (TIQuoteExpr eTI), s)-  IQuoteSymbolExpr e -> do-    (eTI, s) <- inferIExprWithContext e ctx-    return (mkTIExpr (tiExprType eTI) (TIQuoteSymbolExpr eTI), s)-  -  -- Indexed expression (tensor indexing)-  IIndexedExpr override baseExpr indices -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    -- Special handling for IVarExpr: lookup with Var including index info-    -- Use the same strategy as refVar in Data.hs (Core.hs:235)-    (baseTI, s) <- case baseExpr of-      IVarExpr varName -> do-        -- Convert indices to index types (structure only, no content)-        -- Like: map (fmap (const Nothing)) indices in Core.hs-        let indexTypes = map (fmap (const Nothing)) indices-            varWithIndices = Var varName indexTypes-        env <- getEnv-        -- lookupEnv will try: Var "e" [Sub Nothing, Sub Nothing]-        --                 -> Var "e" [Sub Nothing]-        --                 -> Var "e" []-        case lookupEnv varWithIndices env of-          Just scheme -> do-            st <- get-            let (constraints, t, newCounter) = instantiate scheme (inferCounter st)-            modify $ \s' -> s' { inferCounter = newCounter }-            addConstraints constraints-            return (TIExpr (Forall [] constraints t) (TIVarExpr varName), emptySubst)-          Nothing -> do-            -- No variable found in type environment - fall back to normal inference-            -- This is necessary for lambda parameters, let-bound variables, etc.-            inferIExprWithContext baseExpr exprCtx-      _ -> inferIExprWithContext baseExpr exprCtx-    let baseType = tiExprType baseTI-    -- Infer indices as TIExpr-    indicesTI <- mapM (traverse (\idxExpr -> do-      (idxTI, _) <- inferIExprWithContext idxExpr exprCtx-      return idxTI)) indices-    -- Check if all indices are concrete (constants) or symbolic (variables)-    let isSymbolicIndex idx = case idx of-          Sub (TIExpr _ (TIVarExpr _)) -> True-          Sup (TIExpr _ (TIVarExpr _)) -> True-          SupSub (TIExpr _ (TIVarExpr _)) -> True-          User (TIExpr _ (TIVarExpr _)) -> True-          _ -> False-        hasSymbolicIndex = any isSymbolicIndex indicesTI-    -- For tensors with symbolic indices, keep the tensor type-    -- For concrete indices (numeric), return element type-    let resultType = case baseType of-          TTensor elemType -> -            if hasSymbolicIndex-              then TTensor elemType  -- Symbolic index: keep tensor type-              else elemType           -- Concrete index: element access-          TCollection elemType -> elemType-          THash _keyType valType -> valType  -- Hash access returns value type-          _ -> baseType  -- Fallback: return base type-    return (mkTIExpr resultType (TIIndexedExpr override baseTI indicesTI), s)-  -  -- Subrefs expression (subscript references)-  ISubrefsExpr override baseExpr refExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (baseTI, s1) <- inferIExprWithContext baseExpr exprCtx-    (refTI, s2) <- inferIExprWithContext refExpr exprCtx-    let baseType = tiExprType baseTI-        finalS = composeSubst s2 s1-        -- Subrefs requires base to be a Tensor type-        -- Force base type to be Tensor if not already-        tensorBaseType = case baseType of-          TTensor elemType -> TTensor elemType  -- Already Tensor-          otherType -> TTensor otherType  -- Wrap non-Tensor in Tensor-        -- Result is also a Tensor type-        resultType = tensorBaseType-    return (mkTIExpr resultType (TISubrefsExpr override baseTI refTI), finalS)-  -  -- Suprefs expression (superscript references)-  ISuprefsExpr override baseExpr refExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (baseTI, s1) <- inferIExprWithContext baseExpr exprCtx-    (refTI, s2) <- inferIExprWithContext refExpr exprCtx-    let baseType = tiExprType baseTI-        finalS = composeSubst s2 s1-        -- Suprefs requires base to be a Tensor type-        -- Force base type to be Tensor if not already-        tensorBaseType = case baseType of-          TTensor elemType -> TTensor elemType  -- Already Tensor-          otherType -> TTensor otherType  -- Wrap non-Tensor in Tensor-        -- Result is also a Tensor type-        resultType = tensorBaseType-    return (mkTIExpr resultType (TISuprefsExpr override baseTI refTI), finalS)-  -  -- Userrefs expression (user-defined references)-  IUserrefsExpr override baseExpr refExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (baseTI, s1) <- inferIExprWithContext baseExpr exprCtx-    (refTI, s2) <- inferIExprWithContext refExpr exprCtx-    let baseType = tiExprType baseTI-        finalS = composeSubst s2 s1-    -- TODO: Properly handle user-defined references-    return (mkTIExpr baseType (TIUserrefsExpr override baseTI refTI), finalS)--  -- Generate tensor expression-  IGenerateTensorExpr funcExpr shapeExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (funcTI, s1) <- inferIExprWithContext funcExpr exprCtx-    (shapeTI, s2) <- inferIExprWithContext shapeExpr exprCtx-    let funcType = tiExprType funcTI-    -- Extract element type from function result-    elemType <- case funcType of-      TFun _ resultType -> return resultType-      _ -> freshVar "tensorElem"-    let finalS = composeSubst s2 s1-    elemType' <- applySubstWithConstraintsM finalS elemType-    let resultType = normalizeTensorType (TTensor elemType')-    return (mkTIExpr resultType (TIGenerateTensorExpr funcTI shapeTI), finalS)-  -  -- Tensor expression-  ITensorExpr shapeExpr elemsExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (shapeTI, s1) <- inferIExprWithContext shapeExpr exprCtx-    (elemsTI, s2) <- inferIExprWithContext elemsExpr exprCtx-    let elemsType = tiExprType elemsTI-    -- Extract element type-    elemType <- case elemsType of-      TCollection t -> return t-      _ -> freshVar "tensorElem"-    let finalS = composeSubst s2 s1-    elemType' <- applySubstWithConstraintsM finalS elemType-    let resultType = normalizeTensorType (TTensor elemType')-    return (mkTIExpr resultType (TITensorExpr shapeTI elemsTI), finalS)-  -  -- Tensor contract expression-  ITensorContractExpr tensorExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (tensorTI, s1) <- inferIExprWithContext tensorExpr exprCtx-    let tensorType = tiExprType tensorTI-    -    -- contract : Tensor a -> [Tensor a]-    -- Ensure the argument is a Tensor type by unifying with TTensor elemType-    elemType <- freshVar "contractElem"-    tensorType' <- applySubstWithConstraintsM s1 tensorType-    s2 <- unifyTypesWithContext tensorType' (TTensor elemType) exprCtx--    let finalS = composeSubst s2 s1-    finalElemType <- applySubstWithConstraintsM finalS elemType-    let resultType = TCollection (TTensor finalElemType)-    updatedTensorTI <- applySubstToTIExprM finalS tensorTI--    return (mkTIExpr resultType (TITensorContractExpr updatedTensorTI), finalS)-  -  -- Tensor map expression-  ITensorMapExpr func tensorExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (funcTI, s1) <- inferIExprWithContext func exprCtx-    (tensorTI, s2) <- inferIExprWithContext tensorExpr exprCtx-    let funcType = tiExprType funcTI-        tensorType = tiExprType tensorTI-        s12 = composeSubst s2 s1-    -- Function maps elements: a -> b, tensor is Tensor a, result is Tensor b-    case tensorType of-      TTensor elemType -> do-        resultElemType <- freshVar "tmapElem"-        funcType' <- applySubstWithConstraintsM s12 funcType-        s3 <- unifyTypesWithContext funcType' (TFun elemType resultElemType) exprCtx-        let finalS = composeSubst s3 s12-        resultElemType' <- applySubstWithConstraintsM finalS resultElemType-        let resultType = normalizeTensorType (TTensor resultElemType')-        updatedFuncTI <- applySubstToTIExprM finalS funcTI-        updatedTensorTI <- applySubstToTIExprM finalS tensorTI-        return (mkTIExpr resultType (TITensorMapExpr updatedFuncTI updatedTensorTI), finalS)-      _ -> do-        updatedFuncTI <- applySubstToTIExprM s12 funcTI-        updatedTensorTI <- applySubstToTIExprM s12 tensorTI-        return (mkTIExpr tensorType (TITensorMapExpr updatedFuncTI updatedTensorTI), s12)-  -  -- Tensor map2 expression (binary map)-  ITensorMap2Expr func tensor1 tensor2 -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (funcTI, s1) <- inferIExprWithContext func exprCtx-    (tensor1TI, s2) <- inferIExprWithContext tensor1 exprCtx-    (tensor2TI, s3) <- inferIExprWithContext tensor2 exprCtx-    let funcType = tiExprType funcTI-        t1Type = tiExprType tensor1TI-        t2Type = tiExprType tensor2TI-        s123 = foldr composeSubst emptySubst [s3, s2, s1]-    -- Function: a -> b -> c, tensors are Tensor a and Tensor b, result is Tensor c-    case (t1Type, t2Type) of-      (TTensor elem1, TTensor elem2) -> do-        resultElemType <- freshVar "tmap2Elem"-        funcType' <- applySubstWithConstraintsM s123 funcType-        s4 <- unifyTypesWithContext funcType'-                (TFun elem1 (TFun elem2 resultElemType)) exprCtx-        let finalS = composeSubst s4 s123-        resultElemType' <- applySubstWithConstraintsM finalS resultElemType-        let resultType = normalizeTensorType (TTensor resultElemType')-        updatedFuncTI <- applySubstToTIExprM finalS funcTI-        updatedTensor1TI <- applySubstToTIExprM finalS tensor1TI-        updatedTensor2TI <- applySubstToTIExprM finalS tensor2TI-        return (mkTIExpr resultType (TITensorMap2Expr updatedFuncTI updatedTensor1TI updatedTensor2TI), finalS)-      _ -> do-        updatedFuncTI <- applySubstToTIExprM s123 funcTI-        updatedTensor1TI <- applySubstToTIExprM s123 tensor1TI-        updatedTensor2TI <- applySubstToTIExprM s123 tensor2TI-        return (mkTIExpr t1Type (TITensorMap2Expr updatedFuncTI updatedTensor1TI updatedTensor2TI), s123)-  -  -- Transpose expression-  -- ITransposeExpr takes (permutation, tensor) to match tTranspose signature-  ITransposeExpr permExpr tensorExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (permTI, s) <- inferIExprWithContext permExpr exprCtx-    let permType = tiExprType permTI-    -- Unify permutation type with [MathExpr]-    permType' <- applySubstWithConstraintsM s permType-    s2 <- unifyTypesWithContext permType' (TCollection TMathExpr) exprCtx-    (tensorTI, s3) <- inferIExprWithContext tensorExpr exprCtx-    let finalS = composeSubst s3 (composeSubst s2 s)-    updatedPermTI <- applySubstToTIExprM finalS permTI-    updatedTensorTI <- applySubstToTIExprM finalS tensorTI-    let tensorType = tiExprType updatedTensorTI-    -- Transpose preserves tensor type-    return (mkTIExpr (normalizeTensorType tensorType) (TITransposeExpr updatedPermTI updatedTensorTI), finalS)--  -- Flip indices expression-  IFlipIndicesExpr tensorExpr -> do-    let exprCtx = withExpr (prettyStr expr) ctx-    (tensorTI, s) <- inferIExprWithContext tensorExpr exprCtx-    updatedTensorTI <- applySubstToTIExprM s tensorTI-    let tensorType = tiExprType updatedTensorTI-    -- Flipping indices preserves tensor type-    return (mkTIExpr (normalizeTensorType tensorType) (TIFlipIndicesExpr updatedTensorTI), s)-  -  -- Function symbol expression-  IFunctionExpr names -> do-    -- Function symbols are mathematical function symbols (e.g., f(x,y))-    -- They are represented as MathExpr type-    return (mkTIExpr TMathExpr (TIFunctionExpr names), emptySubst)---- | Infer match clauses type--- All clauses should return the same type--- NEW: Returns TIMatchClause list in addition to type and subst-inferMatchClauses :: TypeErrorContext -> Type -> [IMatchClause] -> Subst -> Infer (Type, [TIMatchClause], Subst)-inferMatchClauses ctx matchedType clauses initSubst = do-  case clauses of-    [] -> do-      -- No clauses (should not happen)-      ty <- freshVar "clauseResult"-      return (ty, [], initSubst)-    (firstClause:restClauses) -> do-      -- Infer first clause-      (firstTI, firstType, s1) <- inferMatchClause ctx matchedType firstClause initSubst-      -      -- Infer rest clauses and unify with first-      (finalType, clauseTIs, finalSubst) <- foldM (inferAndUnifyClause ctx matchedType) (firstType, [firstTI], s1) restClauses-      return (finalType, reverse clauseTIs, finalSubst)-  where-    inferAndUnifyClause :: TypeErrorContext -> Type -> (Type, [TIMatchClause], Subst) -> IMatchClause -> Infer (Type, [TIMatchClause], Subst)-    inferAndUnifyClause ctx' matchedTy (expectedType, accClauses, accSubst) clause = do-      matchedTy' <- applySubstWithConstraintsM accSubst matchedTy-      (clauseTI, clauseType, s1) <- inferMatchClause ctx' matchedTy' clause accSubst-      expectedType' <- applySubstWithConstraintsM s1 expectedType-      s2 <- unifyTypesWithContext expectedType' clauseType ctx'-      let finalS = composeSubst s2 (composeSubst s1 accSubst)-      finalExpectedType <- applySubstWithConstraintsM finalS expectedType-      return (finalExpectedType, clauseTI : accClauses, finalS)---- | Infer a single match clause--- NEW: Returns TIMatchClause in addition to type and subst-inferMatchClause :: TypeErrorContext -> Type -> IMatchClause -> Subst -> Infer (TIMatchClause, Type, Subst)-inferMatchClause ctx matchedType (pattern, bodyExpr) initSubst = do-  -- Infer pattern type and extract pattern variable bindings-  -- Use pattern constructor and pattern function type information-  (tiPattern, bindings, s_pat) <- inferIPattern pattern matchedType ctx-  let s1 = composeSubst s_pat initSubst-  -  -- Convert bindings to TypeScheme format-  let schemes = [(var, Forall [] [] ty) | (var, ty) <- bindings]-  -  -- Infer body expression type with pattern variables in scope-  (bodyTI, s2) <- withEnv schemes $ inferIExprWithContext bodyExpr ctx-  let bodyType = tiExprType bodyTI-      finalS = composeSubst s2 s1-  finalBodyType <- applySubstWithConstraintsM finalS bodyType-  return ((tiPattern, bodyTI), finalBodyType, finalS)---- | Infer multiple patterns left-to-right, making left bindings available to right patterns--- This enables non-linear patterns like ($p, #(p + 1))--- Returns (list of TIPattern, accumulated bindings, substitution)-inferPatternsLeftToRight :: [IPattern] -> [Type] -> [(String, Type)] -> Subst -> TypeErrorContext -                         -> Infer ([TIPattern], [(String, Type)], Subst)-inferPatternsLeftToRight [] [] accBindings accSubst _ctx = -  return ([], accBindings, accSubst)-inferPatternsLeftToRight (p:ps) (t:ts) accBindings accSubst ctx = do-  -- Add accumulated bindings to environment for this pattern-  let schemes = [(var, Forall [] [] ty) | (var, ty) <- accBindings]--  -- Infer this pattern with left bindings in scope-  t' <- applySubstWithConstraintsM accSubst t-  (tipat, newBindings, s) <- withEnv schemes $ inferIPattern p t' ctx--  -- Compose substitutions-  let accSubst' = composeSubst s accSubst--  -- Apply substitution to accumulated bindings-  accBindings'' <- mapM (\(v, ty) -> do-      ty' <- applySubstWithConstraintsM s ty-      return (v, ty')) accBindings-  let accBindings' = accBindings'' ++ newBindings-  -  -- Continue with remaining patterns-  (restTipats, finalBindings, finalSubst) <- inferPatternsLeftToRight ps ts accBindings' accSubst' ctx-  return (tipat : restTipats, finalBindings, finalSubst)-inferPatternsLeftToRight _ _ accBindings accSubst _ = -  return ([], accBindings, accSubst)  -- Mismatched lengths---- | Infer IPattern type and extract pattern variable bindings--- Returns (TIPattern, bindings, substitution)--- bindings: [(variable name, type)]-inferIPattern :: IPattern -> Type -> TypeErrorContext -> Infer (TIPattern, [(String, Type)], Subst)-inferIPattern pat expectedType ctx = case pat of-  IWildCard -> do-    -- Wildcard: no bindings-    let tipat = TIPattern (Forall [] [] expectedType) TIWildCard-    return (tipat, [], emptySubst)-  -  IPatVar name -> do-    -- Pattern variable: bind to expected type-    let tipat = TIPattern (Forall [] [] expectedType) (TIPatVar name)-    return (tipat, [(name, expectedType)], emptySubst)-  -  IValuePat expr -> do-    -- Value pattern: infer expression type and unify with expected type-    (exprTI, s) <- inferIExprWithContext expr ctx-    let exprType = tiExprType exprTI-    exprType' <- applySubstWithConstraintsM s exprType-    expectedType' <- applySubstWithConstraintsM s expectedType-    s' <- unifyTypesWithContext exprType' expectedType' ctx-    let finalS = composeSubst s' s-    exprTI' <- applySubstToTIExprM finalS exprTI-    finalType <- applySubstWithConstraintsM finalS expectedType-    let tipat = TIPattern (Forall [] [] finalType) (TIValuePat exprTI')-    return (tipat, [], finalS)--  IPredPat expr -> do-    -- Predicate pattern: infer predicate expression-    -- Expected type for predicate is: expectedType -> Bool-    let predicateType = TFun expectedType TBool-    (exprTI, s) <- inferIExprWithContext expr ctx-    -- Unify with expected predicate type to concretize type variables-    exprType' <- applySubstWithConstraintsM s (tiExprType exprTI)-    predicateType' <- applySubstWithConstraintsM s predicateType-    s' <- unifyTypesWithContext exprType' predicateType' ctx-    let finalS = composeSubst s' s-    exprTI' <- applySubstToTIExprM finalS exprTI-    finalType <- applySubstWithConstraintsM finalS expectedType-    let tipat = TIPattern (Forall [] [] finalType) (TIPredPat exprTI')-    return (tipat, [], finalS)-  -  ITuplePat pats -> do-    -- Tuple pattern: decompose expected type-    case expectedType of-      TTuple types | length types == length pats -> do-        -- Types match: infer each sub-pattern left-to-right-        -- Left patterns' bindings are available for right patterns (for non-linear patterns)-        (tipats, allBindings, s) <- inferPatternsLeftToRight pats types [] emptySubst ctx-        finalType <- applySubstWithConstraintsM s expectedType-        let tipat = TIPattern (Forall [] [] finalType) (TITuplePat tipats)-        return (tipat, allBindings, s)-      -      TVar _ -> do-        -- Expected type is a type variable: create tuple type-        elemTypes <- mapM (\_ -> freshVar "elem") pats-        let tupleTy = TTuple elemTypes-        s <- unifyTypesWithContext expectedType tupleTy ctx--        -- Recursively infer each sub-pattern left-to-right-        elemTypes' <- mapM (applySubstWithConstraintsM s) elemTypes-        (tipats, allBindings, s') <- inferPatternsLeftToRight pats elemTypes' [] s ctx-        finalType <- applySubstWithConstraintsM s' expectedType-        let tipat = TIPattern (Forall [] [] finalType) (TITuplePat tipats)-        return (tipat, allBindings, s')-      -      _ -> do-        -- Type mismatch-        throwError $ TE.TypeMismatch-          (TTuple (replicate (length pats) (TVar (TyVar "a"))))-          expectedType-          "Tuple pattern but matched type is not a tuple"-          ctx-  -  IInductivePat name pats -> do-    -- Inductive pattern: look up pattern constructor type from pattern environment-    patternEnv <- getPatternEnv-    case lookupPatternEnv name patternEnv of-      Just scheme -> do-        -- Found in pattern environment: use the declared type-        st <- get-        let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)-        modify $ \s -> s { inferCounter = newCounter }-        -        -- Pattern constructor type: arg1 -> arg2 -> ... -> resultType-        let (argTypes, resultType) = extractFunctionArgs ctorType-        -        -- Check argument count matches-        if length argTypes /= length pats-          then throwError $ TE.TypeMismatch-                 (foldr TFun resultType (replicate (length pats) (TVar (TyVar "a"))))-                 ctorType-                 ("Pattern constructor " ++ name ++ " expects " ++ show (length argTypes) -                  ++ " arguments, but got " ++ show (length pats))-                 ctx-          else do-            -- Unify result type with expected type-            s0 <- unifyTypesWithContext resultType expectedType ctx-            argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes--            -- Recursively infer each sub-pattern left-to-right-            -- Left patterns' bindings are available for right patterns-            (tipats, allBindings, s) <- inferPatternsLeftToRight pats argTypes' [] s0 ctx-            finalType <- applySubstWithConstraintsM s expectedType-            let tipat = TIPattern (Forall [] [] finalType) (TIInductivePat name tipats)-            return (tipat, allBindings, s)-      -      Nothing -> do-        -- Not found in pattern environment: try data constructor from value environment-        -- This handles data constructors used as patterns-        env <- getEnv-        case lookupEnv (stringToVar name) env of-          Just scheme -> do-            st <- get-            let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)-            modify $ \s -> s { inferCounter = newCounter }-            -            let (argTypes, resultType) = extractFunctionArgs ctorType-            -            if length argTypes /= length pats-              then throwError $ TE.TypeMismatch-                     (foldr TFun resultType (replicate (length pats) (TVar (TyVar "a"))))-                     ctorType-                     ("Constructor " ++ name ++ " expects " ++ show (length argTypes) -                      ++ " arguments, but got " ++ show (length pats))-                     ctx-              else do-                s0 <- unifyTypesWithContext resultType expectedType ctx-                argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes--                -- Recursively infer each sub-pattern left-to-right-                (tipats, allBindings, s) <- inferPatternsLeftToRight pats argTypes' [] s0 ctx-                finalType <- applySubstWithConstraintsM s expectedType-                let tipat = TIPattern (Forall [] [] finalType) (TIInductivePat name tipats)-                return (tipat, allBindings, s)-          -          Nothing -> do-            -- Not found: generic inference-            argTypes <- mapM (\_ -> freshVar "arg") pats-            let resultType = TInductive name argTypes--            s0 <- unifyTypesWithContext resultType expectedType ctx-            argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes--            -- Recursively infer each sub-pattern left-to-right-            (tipats, allBindings, s) <- inferPatternsLeftToRight pats argTypes' [] s0 ctx-            finalType <- applySubstWithConstraintsM s expectedType-            let tipat = TIPattern (Forall [] [] finalType) (TIInductivePat name tipats)-            return (tipat, allBindings, s)-  -  IIndexedPat p indices -> do-    -- Indexed pattern: infer base pattern and index expressions-    -- For $x_i pattern, x should have type Hash keyType expectedType-    -- where expectedType is the type of the indexed result-    -    -- First, infer the index expressions to determine their types-    indexTypes <- mapM (\_ -> freshVar "idx") indices-    (indexTIs, s1) <- foldM (\(accTIs, accS) (idx, idxType) -> do-      (idxTI, idxS) <- inferIExprWithContext idx ctx-      let actualIdxType = tiExprType idxTI-      actualIdxType' <- applySubstWithConstraintsM idxS actualIdxType-      idxType' <- applySubstWithConstraintsM idxS idxType-      s' <- unifyTypesWithContext actualIdxType' idxType' ctx-      let finalS = composeSubst s' (composeSubst idxS accS)-      return (accTIs ++ [idxTI], finalS)) ([], emptySubst) (zip indices indexTypes)--    -- Construct the base type: Hash indexType expectedType-    -- For simplicity, assume single index access and use THash-    indexType <- case indexTypes of-                   [t] -> applySubstWithConstraintsM s1 t-                   _ -> return TInt  -- Multiple indices: fallback to Int-    let baseType = THash indexType expectedType--    -- Infer base pattern with Hash type-    baseType' <- applySubstWithConstraintsM s1 baseType-    (tipat, bindings, s2) <- inferIPattern p baseType' ctx--    let finalS = composeSubst s2 s1-    finalType <- applySubstWithConstraintsM finalS expectedType-    let tiIndexedPat = TIPattern (Forall [] [] finalType) (TIIndexedPat tipat indexTIs)-    return (tiIndexedPat, bindings, finalS)-  -  ILetPat bindings p -> do-    -- Let pattern: infer bindings and then the pattern-    -- Infer bindings first-    env <- getEnv-    (bindingTIs, bindingSchemes, s1) <- inferIBindingsWithContext bindings env emptySubst ctx--    -- Infer pattern with bindings in scope-    expectedType' <- applySubstWithConstraintsM s1 expectedType-    (tipat, patBindings, s2) <- withEnv bindingSchemes $ inferIPattern p expectedType' ctx--    let s = composeSubst s2 s1-    finalType <- applySubstWithConstraintsM s expectedType-    let tiLetPat = TIPattern (Forall [] [] finalType) (TILetPat bindingTIs tipat)-    -- Let bindings are not exported, only pattern bindings-    return (tiLetPat, patBindings, s)-  -  INotPat p -> do-    -- Not pattern: infer the sub-pattern but don't use its bindings-    (tipat, _, s) <- inferIPattern p expectedType ctx-    finalType <- applySubstWithConstraintsM s expectedType-    let tiNotPat = TIPattern (Forall [] [] finalType) (TINotPat tipat)-    return (tiNotPat, [], s)-  -  IAndPat p1 p2 -> do-    -- And pattern: both patterns must match the same type-    -- Left bindings should be available to right pattern-    (tipat1, bindings1, s1) <- inferIPattern p1 expectedType ctx-    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]-    expectedType' <- applySubstWithConstraintsM s1 expectedType-    (tipat2, bindings2, s2) <- withEnv schemes1 $ inferIPattern p2 expectedType' ctx-    let s = composeSubst s2 s1-    -- Apply substitution to left bindings-    bindings1'' <- mapM (\(v, ty) -> do-        ty' <- applySubstWithConstraintsM s2 ty-        return (v, ty')) bindings1-    finalType <- applySubstWithConstraintsM s expectedType-    let bindings1' = bindings1''-        tiAndPat = TIPattern (Forall [] [] finalType) (TIAndPat tipat1 tipat2)-    return (tiAndPat, bindings1' ++ bindings2, s)-  -  IOrPat p1 p2 -> do-    -- Or pattern: both patterns must match the same type-    -- Left bindings should be available to right pattern for non-linear patterns-    (tipat1, bindings1, s1) <- inferIPattern p1 expectedType ctx-    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]-    expectedType' <- applySubstWithConstraintsM s1 expectedType-    (tipat2, bindings2, s2) <- withEnv schemes1 $ inferIPattern p2 expectedType' ctx-    let s = composeSubst s2 s1-    -- Apply substitution to left bindings-    bindings1'' <- mapM (\(v, ty) -> do-        ty' <- applySubstWithConstraintsM s2 ty-        return (v, ty')) bindings1-    finalType <- applySubstWithConstraintsM s expectedType-    let bindings1' = bindings1''-        tiOrPat = TIPattern (Forall [] [] finalType) (TIOrPat tipat1 tipat2)-    -- For or patterns, ideally both branches should have same variables-    -- For now, we take union of bindings-    return (tiOrPat, bindings1' ++ bindings2, s)-  -  IForallPat p1 p2 -> do-    -- Forall pattern: similar to and pattern-    -- Left bindings should be available to right pattern-    (tipat1, bindings1, s1) <- inferIPattern p1 expectedType ctx-    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]-    expectedType' <- applySubstWithConstraintsM s1 expectedType-    (tipat2, bindings2, s2) <- withEnv schemes1 $ inferIPattern p2 expectedType' ctx-    let s = composeSubst s2 s1-    -- Apply substitution to left bindings-    bindings1'' <- mapM (\(v, ty) -> do-        ty' <- applySubstWithConstraintsM s2 ty-        return (v, ty')) bindings1-    finalType <- applySubstWithConstraintsM s expectedType-    let bindings1' = bindings1''-        tiForallPat = TIPattern (Forall [] [] finalType) (TIForallPat tipat1 tipat2)-    return (tiForallPat, bindings1' ++ bindings2, s)-  -  ILoopPat var range p1 p2 -> do-    -- Loop pattern: $var is the loop variable (Integer), range contains pattern-    -- First, infer the range pattern (third element of ILoopRange)-    let ILoopRange startExpr endExpr rangePattern = range-    (tiRangePat, rangeBindings, s_range) <- inferIPattern rangePattern TInt ctx-    -    -- Infer start and end expressions-    (startTI, s_start) <- inferIExprWithContext startExpr ctx-    (endTI, s_end) <- inferIExprWithContext endExpr ctx-    let tiLoopRange = TILoopRange startTI endTI tiRangePat-    -    -- Add loop variable binding (always Integer for loop index)-    let loopVarBinding = (var, TInt)-        initialBindings = loopVarBinding : rangeBindings-        schemes0 = [(v, Forall [] [] ty) | (v, ty) <- initialBindings]-        s_combined = foldr composeSubst emptySubst [s_end, s_start, s_range]--    -- Infer p1 with loop variable and range bindings in scope-    expectedType1 <- applySubstWithConstraintsM s_combined expectedType-    (tipat1, bindings1, s1) <- withEnv schemes0 $ inferIPattern p1 expectedType1 ctx--    -- Infer p2 with all previous bindings in scope-    allPrevBindings' <- mapM (\(v, ty) -> do-        ty' <- applySubstWithConstraintsM s1 ty-        return (v, ty')) initialBindings-    let allPrevBindings = allPrevBindings' ++ bindings1-        schemes1 = [(v, Forall [] [] ty) | (v, ty) <- allPrevBindings]-    expectedType2 <- applySubstWithConstraintsM s1 expectedType-    (tipat2, bindings2, s2) <- withEnv schemes1 $ inferIPattern p2 expectedType2 ctx-    -    let s = foldr composeSubst emptySubst [s2, s1, s_combined]-    -- Apply final substitution to all bindings-    finalBindings' <- mapM (\(v, ty) -> do-        ty' <- applySubstWithConstraintsM s ty-        return (v, ty')) (loopVarBinding : rangeBindings ++ bindings1 ++ bindings2)-    finalType <- applySubstWithConstraintsM s expectedType-    let finalBindings = finalBindings'-        tiLoopPat = TIPattern (Forall [] [] finalType) (TILoopPat var tiLoopRange tipat1 tipat2)--    return (tiLoopPat, finalBindings, s)-  -  IContPat -> do-    -- Continuation pattern: no bindings-    let tipat = TIPattern (Forall [] [] expectedType) TIContPat-    return (tipat, [], emptySubst)-  -  IPApplyPat funcExpr argPats -> do-    -- Pattern application: infer pattern function type-    (funcTI, s1) <- inferIExprWithContext funcExpr ctx-    -    -- Pattern function should return a pattern that matches expectedType-    -- Infer argument patterns left-to-right with fresh types-    argTypes <- mapM (\_ -> freshVar "parg") argPats-    (tipats, allBindings, s2) <- inferPatternsLeftToRight argPats argTypes [] s1 ctx--    finalType <- applySubstWithConstraintsM s2 expectedType-    let tipat = TIPattern (Forall [] [] finalType) (TIPApplyPat funcTI tipats)-    return (tipat, allBindings, s2)-  -  IVarPat name -> do-    -- Variable pattern (with ~): bind to expected type-    let tipat = TIPattern (Forall [] [] expectedType) (TIVarPat name)-    return (tipat, [(name, expectedType)], emptySubst)-  -  IInductiveOrPApplyPat name pats -> do-    -- Could be either inductive pattern or pattern application-    -- Check pattern function environment to distinguish-    -- Pattern functions are ONLY in patternFuncEnv, pattern constructors are NOT-    patternFuncEnv <- getPatternFuncEnv-    case lookupPatternEnv name patternFuncEnv of-      Just _ -> do-        -- It's a pattern function: treat as pattern application-        (tipat, bindings, s) <- inferIPattern (IPApplyPat (IVarExpr name) pats) expectedType ctx-        return (tipat, bindings, s)-      Nothing -> do-        -- It's an inductive pattern constructor (or not found, will be handled later)-        (tipat, bindings, s) <- inferIPattern (IInductivePat name pats) expectedType ctx-        -- Wrap it as InductiveOrPApplyPat (if it's actually an inductive pattern)-        case tipPatternNode tipat of-          TIInductivePat _ tipats -> do-            let scheme = tipScheme tipat-                tiInductiveOrPApplyPat = TIPattern scheme (TIInductiveOrPApplyPat name tipats)-            return (tiInductiveOrPApplyPat, bindings, s)-          _ -> -            -- Not an inductive pattern (e.g., already processed as pattern application)-            return (tipat, bindings, s)-  -  ISeqNilPat -> do-    -- Sequence nil: no bindings-    let tipat = TIPattern (Forall [] [] expectedType) TISeqNilPat-    return (tipat, [], emptySubst)-  -  ISeqConsPat p1 p2 -> do-    -- Sequence cons: infer both patterns-    -- Left bindings should be available to right pattern-    (tipat1, bindings1, s1) <- inferIPattern p1 expectedType ctx-    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]-    expectedType' <- applySubstWithConstraintsM s1 expectedType-    (tipat2, bindings2, s2) <- withEnv schemes1 $ inferIPattern p2 expectedType' ctx-    let s = composeSubst s2 s1-    -- Apply substitution to left bindings-    bindings1'' <- mapM (\(v, ty) -> do-        ty' <- applySubstWithConstraintsM s2 ty-        return (v, ty')) bindings1-    finalType <- applySubstWithConstraintsM s expectedType-    let bindings1' = bindings1''-        tipat = TIPattern (Forall [] [] finalType) (TISeqConsPat tipat1 tipat2)-    return (tipat, bindings1' ++ bindings2, s)-  -  ILaterPatVar -> do-    -- Later pattern variable: no immediate binding-    let tipat = TIPattern (Forall [] [] expectedType) TILaterPatVar-    return (tipat, [], emptySubst)-  -  IDApplyPat p pats -> do-    -- D-apply pattern: infer base pattern and argument patterns-    -- Base pattern bindings should be available to argument patterns-    (tipat, bindings1, s1) <- inferIPattern p expectedType ctx-    -    -- Infer argument patterns left-to-right with base pattern bindings in scope-    argTypes <- mapM (\_ -> freshVar "darg") pats-    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]-    (tipats, argBindings, s2) <- withEnv schemes1 $ inferPatternsLeftToRight pats argTypes [] s1 ctx-    -    let s = composeSubst s2 s1-    -- Apply substitution to base bindings-    bindings1'' <- mapM (\(v, ty) -> do-        ty' <- applySubstWithConstraintsM s2 ty-        return (v, ty')) bindings1-    finalType <- applySubstWithConstraintsM s expectedType-    let bindings1' = bindings1''-        tiDApplyPat = TIPattern (Forall [] [] finalType) (TIDApplyPat tipat tipats)-    return (tiDApplyPat, bindings1' ++ argBindings, s)-  where-    -- Extract function argument types and result type-    -- e.g., a -> b -> c -> d  =>  ([a, b, c], d)-    extractFunctionArgs :: Type -> ([Type], Type)-    extractFunctionArgs (TFun arg rest) = -      let (args, result) = extractFunctionArgs rest-      in (arg : args, result)-    extractFunctionArgs t = ([], t)---- | Infer application (helper)--- NEW: Returns TIExpr instead of (IExpr, Type, Subst)-inferIApplication :: String -> Type -> [IExpr] -> Subst -> Infer (TIExpr, Subst)-inferIApplication funcName funcType args initSubst = do-  let funcTI = mkTIExpr funcType (TIVarExpr funcName)-  inferIApplicationWithContext funcTI funcType args initSubst emptyContext---- TensorMap insertion logic has been moved to Language.Egison.Type.TensorMapInsertion--- This keeps type inference focused on type checking only---- | Infer application (helper) with context--- NEW: Returns TIExpr instead of (IExpr, Type, Subst)--- TensorMap insertion has been moved to Phase 8 (TensorMapInsertion module)--- This function now only performs type inference and unification--- When a Tensor argument is passed to a scalar parameter, the result type is wrapped in Tensor------ IMPORTANT: Non-function arguments are unified first to let data types (like lists)--- constrain type variables before callback function types are unified.--- This ensures that foldl (+) 0 [t1, t2] properly infers a = Tensor Integer from the list--- before trying to match the callback type.-inferIApplicationWithContext :: TIExpr -> Type -> [IExpr] -> Subst -> TypeErrorContext -> Infer (TIExpr, Subst)-inferIApplicationWithContext funcTIExpr funcType args initSubst ctx = do-  -- Infer argument types-  argResults <- mapM (\arg -> inferIExprWithContext arg ctx) args-  let argTIExprs = map fst argResults-      argTypes = map (tiExprType . fst) argResults-      argSubst = foldr composeSubst initSubst (map snd argResults)--  -- Create fresh type variables for parameters and result-  paramVars <- mapM (\i -> freshVar ("param" ++ show i)) [1..length args]-  resultType <- freshVar "result"-  let expectedFuncType = foldr TFun resultType paramVars-  appliedFuncType <- applySubstWithConstraintsM argSubst funcType---  -- First unify function type structure to get parameter bindings-  let funcScheme = tiScheme funcTIExpr-      (Forall _tvs funcConstraints _) = funcScheme-  classEnv <- getClassEnv-  -- Include constraints from both the function being applied AND the inference context-  -- The context constraints include constraints from outer scopes (e.g., {Num a} from (.) definition)-  contextConstraints <- getConstraints-  let constraints = funcConstraints ++ contextConstraints-  case Unify.unifyWithConstraints classEnv constraints appliedFuncType expectedFuncType of-    Right (s1, flag1) -> do-      -- Now unify argument types with parameter types-      -- Key: Unify non-function arguments FIRST to let data types constrain type variables-      paramTypesRaw <- mapM (applySubstWithConstraintsM s1) paramVars-      let indexedArgs = zip3 [0..] argTypes paramTypesRaw--      -- Classify arguments: non-functions first, then functions-      -- A type is considered a function if it's TFun-          isArgFunction (TFun _ _) = True-          isArgFunction _ = False-          (funcArgsList, nonFuncArgsList) = partition (\(_, at, _) -> isArgFunction at) indexedArgs--      -- Unify non-function arguments first (data types like lists)-      -- IMPORTANT: Apply substitution to constraints so that constraint checking works correctly-      (s2, flag2) <- foldM (\(s, flagAcc) (_, at, pt) -> do-                     at' <- applySubstWithConstraintsM s at-                     pt' <- applySubstWithConstraintsM s pt-                     let cs' = map (applySubstConstraint s) constraints-                     case Unify.unifyWithConstraints classEnv cs' at' pt' of-                       Right (s', flag') -> return (composeSubst s' s, flagAcc || flag')-                       Left _ -> throwError $ UnificationError at' pt' ctx-                  ) (s1, flag1) nonFuncArgsList--      -- Then unify function arguments (callbacks)-      -- IMPORTANT: Include constraints from the argument's type scheme (e.g., {Num t} from (+))-      -- so that constraint checking works correctly for the argument's type variables-      (s3, flag3) <- foldM (\(s, flagAcc) (idx, at, pt) -> do-                     at' <- applySubstWithConstraintsM s at-                     pt' <- applySubstWithConstraintsM s pt-                     let -- Get constraints from both the outer function and the argument itself-                         outerCs = map (applySubstConstraint s) constraints-                         argScheme = tiScheme (argTIExprs !! idx)-                         (Forall _ argConstraints _) = argScheme-                         argCs = map (applySubstConstraint s) argConstraints-                         allCs = outerCs ++ argCs-                     case Unify.unifyWithConstraints classEnv allCs at' pt' of-                       Right (s', flag') -> return (composeSubst s' s, flagAcc || flag')-                       Left _ -> throwError $ UnificationError at' pt' ctx-                  ) (s2, flag2) funcArgsList--      let finalS = composeSubst s3 argSubst-      baseResultType <- applySubstWithConstraintsM finalS resultType--      -- Check if Tensor was unwrapped during unification (flag3)-      -- If so, wrap the result type in Tensor-      -- This handles cases like sum : {Num a} [a] -> a with [Tensor Integer]-      -- where a unifies with Tensor Integer but gets unwrapped to Integer-      let needsTensorWrap = flag3-          finalType = if needsTensorWrap && not (Types.isTensorType baseResultType)-                      then TTensor baseResultType-                      else baseResultType--      -- Apply substitution to constraints and simplify Tensor constraints-      -- This rewrites C (Tensor a) to C a when appropriate, while keeping types as Tensor a-      -- IMPORTANT: Only use funcConstraints for the result scheme, not contextConstraints-      -- contextConstraints are from outer scopes and should not be propagated to sub-expressions-      let updatedFuncConstraints = map (applySubstConstraint finalS) funcConstraints-          simplifiedFuncConstraints = simplifyTensorConstraints classEnv updatedFuncConstraints-          -- Deduplicate constraints-          deduplicatedConstraints = nub simplifiedFuncConstraints-          -- Filter out constraints on concrete types (only keep constraints on type variables)-          -- This prevents constraints like {Num (Tensor t0)} from appearing in result types-          isTypeVarConstraint (Constraint _ (TVar _)) = True-          isTypeVarConstraint _ = False-          typeVarConstraints = filter isTypeVarConstraint deduplicatedConstraints-          -- Result constraints: functions (partial applications) keep constraints,-          -- but values (fully applied) don't need them-          resultConstraints = case finalType of-                                TFun _ _ -> typeVarConstraints  -- Partial application-                                _ -> []  -- Fully applied: no constraints needed-          resultScheme = Forall [] resultConstraints finalType--          -- Update function and argument TIExprs-          -- IMPORTANT: Use applySubstToTIExprWithClassEnv to adjust substitution based on constraints-          -- When {Num t0} t0 -> t0 is unified with Tensor t1, if Num (Tensor t1) has no instance,-          -- the substitution is adjusted to t0 -> t1 (unwrapping the Tensor)-          updatedFuncTI = applySubstToTIExprWithClassEnv classEnv finalS funcTIExpr-          updatedArgTIs = map (applySubstToTIExprWithClassEnv classEnv finalS) argTIExprs--      return (TIExpr resultScheme (TIApplyExpr updatedFuncTI updatedArgTIs), finalS)--    Left _ ->-      -- Special case: if function has type MathExpr, allow application returning MathExpr-      -- (handles FunctionData application, e.g. f 0 where f := function (x))-      case appliedFuncType of-        TMathExpr -> do-          classEnv' <- getClassEnv-          let resultScheme = Forall [] [] TMathExpr-              updatedFuncTI = applySubstToTIExprWithClassEnv classEnv' argSubst funcTIExpr-              updatedArgTIs = map (applySubstToTIExprWithClassEnv classEnv' argSubst) argTIExprs-          return (TIExpr resultScheme (TIApplyExpr updatedFuncTI updatedArgTIs), argSubst)-        _ -> throwError $ UnificationError appliedFuncType expectedFuncType ctx--- | Infer let bindings (non-recursive)---- | Infer let bindings (non-recursive) with context--- NEW: Returns TIBindingExpr instead of IBindingExpr--- Infer IO bindings for do expressions-inferIOBindingsWithContext :: [IBindingExpr] -> TypeEnv -> Subst -> TypeErrorContext -> Infer ([TIBindingExpr], [(String, TypeScheme)], Subst)-inferIOBindingsWithContext [] _env s _ctx = return ([], [], s)-inferIOBindingsWithContext ((pat, expr):bs) env s ctx = do-  -- Infer the type of the expression-  (exprTI, s1) <- inferIExprWithContext expr ctx-  let exprType = tiExprType exprTI--  -- The expression should be of type IO a-  innerType <- freshVar "ioInner"-  exprType' <- applySubstWithConstraintsM s1 exprType-  s2 <- unifyTypesWithContext exprType' (TIO innerType) ctx-  let s12 = composeSubst s2 s1-  actualInnerType <- applySubstWithConstraintsM s12 innerType--  -- Create expected type from pattern and unify with inner type-  (patternType, s3) <- inferPatternType pat-  let s123 = composeSubst s3 s12-  actualInnerType' <- applySubstWithConstraintsM s123 actualInnerType-  patternType' <- applySubstWithConstraintsM s123 patternType-  s4 <- unifyTypesWithContext actualInnerType' patternType' ctx--  -- Apply all substitutions and extract bindings with inner type-  let finalS = composeSubst s4 s123-  finalInnerType <- applySubstWithConstraintsM finalS actualInnerType-  let bindings = extractIBindingsFromPattern pat finalInnerType-      s' = composeSubst finalS s--  _env' <- getEnv-  let extendedEnvList = bindings  -- Already a list of (String, TypeScheme)-  (restBindingTIs, restBindings, s2') <- withEnv extendedEnvList $ inferIOBindingsWithContext bs env s' ctx-  return ((pat, exprTI) : restBindingTIs, bindings ++ restBindings, s2')-  where-    -- Infer the type that a pattern expects-    inferPatternType :: IPrimitiveDataPattern -> Infer (Type, Subst)-    inferPatternType PDWildCard = do-      t <- freshVar "wild"-      return (t, emptySubst)-    inferPatternType (PDPatVar _) = do-      t <- freshVar "patvar"-      return (t, emptySubst)-    inferPatternType (PDTuplePat pats) = do-      results <- mapM inferPatternType pats-      let types = map fst results-          substs = map snd results-          s = foldr composeSubst emptySubst substs-      return (TTuple types, s)-    inferPatternType PDEmptyPat = return (TCollection (TVar (TyVar "a")), emptySubst)-    inferPatternType (PDConsPat _ _) = do-      elemType <- freshVar "elem"-      return (TCollection elemType, emptySubst)-    inferPatternType (PDSnocPat _ _) = do-      elemType <- freshVar "elem"-      return (TCollection elemType, emptySubst)-    inferPatternType (PDInductivePat name pats) = do-      results <- mapM inferPatternType pats-      let types = map fst results-          substs = map snd results-          s = foldr composeSubst emptySubst substs-      return (TInductive name types, s)-    inferPatternType (PDConstantPat c) = do-      ty <- inferConstant c-      return (ty, emptySubst)-    -- ScalarData primitive patterns-    inferPatternType (PDDivPat _ _) = return (TMathExpr, emptySubst)-    inferPatternType (PDPlusPat _) = return (TPolyExpr, emptySubst)-    inferPatternType (PDTermPat _ _) = return (TTermExpr, emptySubst)-    inferPatternType (PDSymbolPat _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply1Pat _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply2Pat _ _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply3Pat _ _ _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply4Pat _ _ _ _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDQuotePat _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDFunctionPat _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDSubPat _) = return (TIndexExpr, emptySubst)-    inferPatternType (PDSupPat _) = return (TIndexExpr, emptySubst)-    inferPatternType (PDUserPat _) = return (TIndexExpr, emptySubst)---- | Apply substitution recursively until a fixed point is reached--- This ensures that nested type variables are fully resolved--- For example, if s = {t1 -> (Integer, t2), t2 -> [Integer]}, then--- applySubstRecursively s t1 will return (Integer, [Integer])--- instead of (Integer, t2)-applySubstRecursively :: Subst -> Type -> Infer Type-applySubstRecursively s t = applySubstRecursively' s t 5  -- Max 5 iterations (reduced from 10)-  where-    applySubstRecursively' :: Subst -> Type -> Int -> Infer Type-    applySubstRecursively' _ t 0 = return t  -- Stop after max iterations-    applySubstRecursively' s t n = do-      t' <- applySubstWithConstraintsM s t-      if t' == t-        then return t-        else applySubstRecursively' s t' (n - 1)--inferIBindingsWithContext :: [IBindingExpr] -> TypeEnv -> Subst -> TypeErrorContext -> Infer ([TIBindingExpr], [(String, TypeScheme)], Subst)-inferIBindingsWithContext [] _env s _ctx = return ([], [], s)-inferIBindingsWithContext ((pat, expr):bs) env s ctx = do-  -- Infer the type of the expression-  (exprTI, s1) <- inferIExprWithContext expr ctx-  let exprType = tiExprType exprTI--  -- Create expected type from pattern and unify with expression type-  -- This helps resolve type variables in the expression type-  (patternType, s2) <- inferPatternType pat-  let s12 = composeSubst s2 s1-  exprType' <- applySubstWithConstraintsM s12 exprType-  patternType' <- applySubstWithConstraintsM s12 patternType-  s3 <- unifyTypesWithContext exprType' patternType' ctx--  -- Apply all substitutions recursively until fixed point-  -- This ensures nested type variables are fully resolved (e.g., for sortWithSign)-  let finalS = composeSubst s3 s12-  finalExprType <- applySubstRecursively finalS exprType-  let bindings = extractIBindingsFromPattern pat finalExprType-      s' = composeSubst finalS s--  _env' <- getEnv-  let extendedEnvList = bindings  -- Already a list of (String, TypeScheme)-  (restBindingTIs, restBindings, s2') <- withEnv extendedEnvList $ inferIBindingsWithContext bs env s' ctx-  return ((pat, exprTI) : restBindingTIs, bindings ++ restBindings, s2')-  where-    -- Infer the type that a pattern expects-    inferPatternType :: IPrimitiveDataPattern -> Infer (Type, Subst)-    inferPatternType PDWildCard = do-      t <- freshVar "wild"-      return (t, emptySubst)-    inferPatternType (PDPatVar _) = do-      t <- freshVar "patvar"-      return (t, emptySubst)-    inferPatternType (PDTuplePat pats) = do-      results <- mapM inferPatternType pats-      let types = map fst results-          substs = map snd results-          s = foldr composeSubst emptySubst substs-      return (TTuple types, s)-    inferPatternType PDEmptyPat = return (TCollection (TVar (TyVar "a")), emptySubst)-    inferPatternType (PDConsPat _ _) = do-      elemType <- freshVar "elem"-      return (TCollection elemType, emptySubst)-    inferPatternType (PDSnocPat _ _) = do-      elemType <- freshVar "elem"-      return (TCollection elemType, emptySubst)-    inferPatternType (PDInductivePat name pats) = do-      results <- mapM inferPatternType pats-      let types = map fst results-          substs = map snd results-          s = foldr composeSubst emptySubst substs-      return (TInductive name types, s)-    inferPatternType (PDConstantPat c) = do-      ty <- inferConstant c-      return (ty, emptySubst)-    -- ScalarData primitive patterns-    inferPatternType (PDDivPat _ _) = return (TMathExpr, emptySubst)-    inferPatternType (PDPlusPat _) = return (TPolyExpr, emptySubst)-    inferPatternType (PDTermPat _ _) = return (TTermExpr, emptySubst)-    inferPatternType (PDSymbolPat _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply1Pat _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply2Pat _ _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply3Pat _ _ _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDApply4Pat _ _ _ _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDQuotePat _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDFunctionPat _ _) = return (TSymbolExpr, emptySubst)-    inferPatternType (PDSubPat _) = return (TIndexExpr, emptySubst)-    inferPatternType (PDSupPat _) = return (TIndexExpr, emptySubst)-    inferPatternType (PDUserPat _) = return (TIndexExpr, emptySubst)---- | Infer letrec bindings (recursive)---- | Infer letrec bindings (recursive) with context--- NEW: Returns TIBindingExpr instead of IBindingExpr-inferIRecBindingsWithContext :: [IBindingExpr] -> TypeEnv -> Subst -> TypeErrorContext -> Infer ([TIBindingExpr], [(String, TypeScheme)], Subst)-inferIRecBindingsWithContext bindings _env s ctx = do-  -- Create placeholders with fresh type variables-  placeholders <- mapM (\(pat, _) -> do-    (patternType, s1) <- inferPatternType pat-    return (pat, patternType, s1)) bindings-  -  let placeholderTypes = map (\(_, ty, _) -> ty) placeholders-      placeholderSubsts = map (\(_, _, s) -> s) placeholders-      s0 = foldr composeSubst s placeholderSubsts-  -  -- Extract bindings from placeholders-  let placeholderBindings = concat $ zipWith (\(pat, _, _) ty -> extractIBindingsFromPattern pat ty) placeholders placeholderTypes-  -  -- Infer expressions in extended environment-  results <- withEnv placeholderBindings $ mapM (\(_, expr) -> inferIExprWithContext expr ctx) bindings-  -  let exprTIs = map fst results-      exprTypes = map (tiExprType . fst) results-      substList = map snd results-      s1 = foldr composeSubst s0 substList-  -  -- Unify placeholder types with inferred expression types-  unifySubsts <- zipWithM (\placeholderTy exprTy -> do-    placeholderTy' <- applySubstWithConstraintsM s1 placeholderTy-    exprTy' <- applySubstWithConstraintsM s1 exprTy-    unifyTypesWithContext exprTy' placeholderTy' ctx) placeholderTypes exprTypes-  -  let finalS = foldr composeSubst s1 unifySubsts--  -- Re-extract bindings with fully resolved types-  exprTypes' <- mapM (applySubstRecursively finalS) exprTypes-  let finalBindings = concat $ zipWith (\(pat, _, _) ty -> extractIBindingsFromPattern pat ty) placeholders exprTypes'-      transformedBindings = zipWith (\(pat, _) exprTI -> (pat, exprTI)) bindings exprTIs--  return (transformedBindings, finalBindings, finalS)-  where-    -- Infer the type that a pattern expects (same as in inferIBindingsWithContext)-    inferPatternType :: IPrimitiveDataPattern -> Infer (Type, Subst)-    inferPatternType PDWildCard = do-      t <- freshVar "wild"-      return (t, emptySubst)-    inferPatternType (PDPatVar _) = do-      t <- freshVar "rec"-      return (t, emptySubst)-    inferPatternType (PDTuplePat pats) = do-      results <- mapM inferPatternType pats-      let types = map fst results-          substs = map snd results-          s = foldr composeSubst emptySubst substs-      return (TTuple types, s)-    inferPatternType PDEmptyPat = return (TCollection (TVar (TyVar "a")), emptySubst)-    inferPatternType (PDConsPat _ _) = do-      elemType <- freshVar "elem"-      return (TCollection elemType, emptySubst)-    inferPatternType (PDSnocPat _ _) = do-      elemType <- freshVar "elem"-      return (TCollection elemType, emptySubst)-    inferPatternType (PDInductivePat name pats) = do-      results <- mapM inferPatternType pats-      let types = map fst results-          substs = map snd results-          s = foldr composeSubst emptySubst substs-      return (TInductive name types, s)-    inferPatternType (PDConstantPat c) = do-      ty <- inferConstant c-      return (ty, emptySubst)-    -- Add other cases as needed-    inferPatternType _ = do-      t <- freshVar "rec"-      return (t, emptySubst)---- | Extract bindings from pattern--- This function extracts variable bindings from a primitive data pattern--- given the type that the pattern should match against--- Helper to check if a pattern is a pattern variable-isPatVarPat :: IPrimitiveDataPattern -> Bool-isPatVarPat (PDPatVar _) = True-isPatVarPat _ = False--extractIBindingsFromPattern :: IPrimitiveDataPattern -> Type -> [(String, TypeScheme)]-extractIBindingsFromPattern pat ty = case pat of-  PDWildCard -> []-  PDPatVar var -> [(extractNameFromVar var, Forall [] [] ty)]-  PDInductivePat _ pats -> concatMap (\p -> extractIBindingsFromPattern p ty) pats-  PDTuplePat pats -> -    case ty of-      TTuple tys | length pats == length tys -> -        -- Types match: bind each pattern variable to corresponding type-        concat $ zipWith extractIBindingsFromPattern pats tys-      _ -> -        -- Type is not a resolved tuple (might be type variable or mismatch)-        -- Extract pattern variables but assign them the full tuple type for now-        -- This is imprecise but allows variables to be in scope-        -- The actual element types will be determined during later unification-        concatMap (\p -> extractIBindingsFromPattern p ty) pats-  PDEmptyPat -> []-  PDConsPat p1 p2 ->-    case ty of-      TCollection elemTy -> extractIBindingsFromPattern p1 elemTy ++ extractIBindingsFromPattern p2 ty-      _ -> []-  PDSnocPat p1 p2 ->-    case ty of-      TCollection elemTy -> extractIBindingsFromPattern p1 ty ++ extractIBindingsFromPattern p2 elemTy-      _ -> []-  -- ScalarData primitive patterns-  PDDivPat p1 p2 ->-    let polyExprTy = TPolyExpr-        mathExprTy = TMathExpr-        p1Ty = if isPatVarPat p1 then mathExprTy else polyExprTy-        p2Ty = if isPatVarPat p2 then mathExprTy else polyExprTy-    in extractIBindingsFromPattern p1 p1Ty ++ extractIBindingsFromPattern p2 p2Ty-  PDPlusPat p ->-    let termExprTy = TTermExpr-        mathExprTy = TMathExpr-        pTy = if isPatVarPat p then TCollection mathExprTy else TCollection termExprTy-    in extractIBindingsFromPattern p pTy-  PDTermPat p1 p2 ->-    let symbolExprTy = TSymbolExpr-        mathExprTy = TMathExpr-        p2Ty = if isPatVarPat p2-               then TCollection (TTuple [mathExprTy, TInt])-               else TCollection (TTuple [symbolExprTy, TInt])-    in extractIBindingsFromPattern p1 TInt ++ extractIBindingsFromPattern p2 p2Ty-  PDSymbolPat p1 p2 ->-    let indexExprTy = TIndexExpr-    in extractIBindingsFromPattern p1 TString ++ extractIBindingsFromPattern p2 (TCollection indexExprTy)-  PDApply1Pat p1 p2 ->-    let mathExprTy = TMathExpr-        fnTy = TFun mathExprTy mathExprTy-    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathExprTy-  PDApply2Pat p1 p2 p3 ->-    let mathExprTy = TMathExpr-        fnTy = TFun mathExprTy (TFun mathExprTy mathExprTy)-    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathExprTy ++ extractIBindingsFromPattern p3 mathExprTy-  PDApply3Pat p1 p2 p3 p4 ->-    let mathExprTy = TMathExpr-        fnTy = TFun mathExprTy (TFun mathExprTy (TFun mathExprTy mathExprTy))-    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathExprTy ++ extractIBindingsFromPattern p3 mathExprTy ++ extractIBindingsFromPattern p4 mathExprTy-  PDApply4Pat p1 p2 p3 p4 p5 ->-    let mathExprTy = TMathExpr-        fnTy = TFun mathExprTy (TFun mathExprTy (TFun mathExprTy (TFun mathExprTy mathExprTy)))-    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathExprTy ++ extractIBindingsFromPattern p3 mathExprTy ++ extractIBindingsFromPattern p4 mathExprTy ++ extractIBindingsFromPattern p5 mathExprTy-  PDQuotePat p ->-    let mathExprTy = TMathExpr-    in extractIBindingsFromPattern p mathExprTy-  PDFunctionPat p1 p2 ->-    let mathExprTy = TMathExpr-    in extractIBindingsFromPattern p1 mathExprTy ++ extractIBindingsFromPattern p2 (TCollection mathExprTy)-  PDSubPat p ->-    let mathExprTy = TMathExpr-    in extractIBindingsFromPattern p mathExprTy-  PDSupPat p ->-    let mathExprTy = TMathExpr-    in extractIBindingsFromPattern p mathExprTy-  PDUserPat p ->-    let mathExprTy = TMathExpr-    in extractIBindingsFromPattern p mathExprTy-  _ -> []---- | Infer top-level IExpr and return TITopExpr directly-inferITopExpr :: ITopExpr -> Infer (Maybe TITopExpr, Subst)-inferITopExpr topExpr = case topExpr of-  IDefine var expr -> do-    varName <- return $ extractNameFromVar var-    env <- getEnv-    -- Check if there's an explicit type signature in the environment-    -- (added by EnvBuilder from DefineWithType)-    case lookupEnv var env of-      Just existingScheme -> do-        -- There's an explicit type signature: check that the inferred type matches-        st <- get-        let (instConstraints, expectedType, newCounter) = instantiate existingScheme (inferCounter st)-        modify $ \s -> s { inferCounter = newCounter }-        -- Add instantiated constraints to the inference context-        -- This is crucial for constraint-aware unification inside the definition body-        -- e.g., when (.) has {Num a}, this constraint must be visible when type-checking t1 * t2-        clearConstraints  -- Start fresh-        addConstraints instConstraints--        -- Infer the expression type-        (exprTI, subst1) <- inferIExpr expr-        let exprType = tiExprType exprTI--        -- Unify inferred type with expected type using constraint-aware unification-        -- This is crucial for cases like (.) where type variables have constraints-        -- The constraints from the type signature affect how Tensor types are unified-        let exprCtx = withExpr (prettyStr expr) emptyContext-            -- Apply substitution to constraints to get current state-            currentConstraints = map (applySubstConstraint subst1) instConstraints-        exprType' <- applySubstWithConstraintsM subst1 exprType-        expectedType' <- applySubstWithConstraintsM subst1 expectedType-        subst2 <- unifyTypesWithConstraints currentConstraints exprType' expectedType' exprCtx-        let finalSubst = composeSubst subst2 subst1--        -- Apply final substitution to exprTI to resolve all type variables-        -- IMPORTANT: Use applySubstToTIExprM to adjust substitution based on constraints-        exprTI' <- applySubstToTIExprM finalSubst exprTI--        -- Resolve constraints in exprTI' (Tensor t0 -> t0)-        classEnv <- getClassEnv-        let exprTI'' = resolveConstraintsInTIExpr classEnv finalSubst exprTI'-        -        -- Reconstruct type scheme from exprTI'' to match actual type variables-        -- Use instantiated constraints and apply final substitution-        -- When there's an explicit type annotation, use the expected type-        -- (with substitutions applied) as the final type, not the inferred type.-        -- This ensures that Tensor types are preserved when explicitly annotated.-        finalType <- applySubstWithConstraintsM finalSubst expectedType-        let constraints' = map (applySubstConstraint finalSubst) instConstraints-            envFreeVars = freeVarsInEnv env-            typeFreeVars = freeTyVars finalType-            genVars = Set.toList $ typeFreeVars `Set.difference` envFreeVars-            updatedScheme = Forall genVars constraints' finalType-        -        -- Keep the updated scheme (with actual type variables) in the environment-        return (Just (TIDefine updatedScheme var exprTI''), finalSubst)-      -      Nothing -> do-        -- No explicit type signature: infer and generalize as before-        clearConstraints  -- Start with fresh constraints for this expression-        (exprTI, subst) <- inferIExpr expr-        let exprType = tiExprType exprTI-        constraints <- getConstraints  -- Collect constraints from type inference-        -        -- Resolve constraints based on available instances-        classEnv <- getClassEnv-        let updatedConstraints = map (resolveConstraintWithInstances classEnv subst) constraints-            -- Filter out constraints on concrete types (non-type-variables)-            -- Concrete constraints don't need to be generalized since the type is already determined-            isTypeVarConstraint (Constraint _ (TVar _)) = True-            isTypeVarConstraint _ = False-            -- Deduplicate constraints (e.g., {Num a, Num a} -> {Num a})-            generalizedConstraints = nub $ filter isTypeVarConstraint updatedConstraints--        -- Generalize with filtered constraints (only type variables)-        let envFreeVars = freeVarsInEnv env-            typeFreeVars = freeTyVars exprType-            genVars = Set.toList $ typeFreeVars `Set.difference` envFreeVars-            scheme = Forall genVars generalizedConstraints exprType-        -        -- Add to environment using the Var directly (preserves index info)-        modify $ \s -> s { inferEnv = extendEnv var scheme (inferEnv s) }-        -        return (Just (TIDefine scheme var exprTI), subst)-  -  ITest expr -> do-    clearConstraints  -- Start with fresh constraints-    (exprTI, subst) <- inferIExpr expr-    -- Constraints are now in state, will be retrieved by Eval.hs-    return (Just (TITest exprTI), subst)-  -  IExecute expr -> do-    clearConstraints  -- Start with fresh constraints-    (exprTI, subst) <- inferIExpr expr-    -- Constraints are now in state, will be retrieved by Eval.hs-    return (Just (TIExecute exprTI), subst)-  -  ILoadFile _path -> return (Nothing, emptySubst)-  ILoad _lib -> return (Nothing, emptySubst)--  IDefineMany bindings -> do-    -- Process each binding in the list-    env <- getEnv-    results <- mapM (inferBinding env) bindings-    let bindingsTI = map fst results-        substs = map snd results-        combinedSubst = foldr composeSubst emptySubst substs-    return (Just (TIDefineMany bindingsTI), combinedSubst)-    where-      inferBinding env (var, expr) = do-        let varName = extractNameFromVar var-        -- Check if there's an existing type signature-        case lookupEnv var env of-          Just existingScheme -> do-            -- With type signature: check type-            st <- get-            let (_, expectedType, newCounter) = instantiate existingScheme (inferCounter st)-            modify $ \s -> s { inferCounter = newCounter }-            -            clearConstraints-            (exprTI, subst1) <- inferIExpr expr-            let exprType = tiExprType exprTI-            exprType' <- applySubstWithConstraintsM subst1 exprType-            expectedType' <- applySubstWithConstraintsM subst1 expectedType-            subst2 <- unifyTypesWithTopLevel exprType' expectedType' emptyContext-            let finalSubst = composeSubst subst2 subst1-            exprTI' <- applySubstToTIExprM finalSubst exprTI-            return ((var, exprTI'), finalSubst)-          -          Nothing -> do-            -- Without type signature: infer and generalize-            clearConstraints-            (exprTI, subst) <- inferIExpr expr-            let exprType = tiExprType exprTI-            constraints <- getConstraints-            -            -- Resolve constraints based on available instances-            classEnv <- getClassEnv-            let updatedConstraints = map (resolveConstraintWithInstances classEnv subst) constraints-                -- Filter out constraints on concrete types (non-type-variables)-                isTypeVarConstraint (Constraint _ (TVar _)) = True-                isTypeVarConstraint _ = False-                -- Deduplicate constraints (e.g., {Num a, Num a} -> {Num a})-                generalizedConstraints = nub $ filter isTypeVarConstraint updatedConstraints--            -- Generalize the type-            let envFreeVars = freeVarsInEnv env-                typeFreeVars = freeTyVars exprType-                genVars = Set.toList $ typeFreeVars `Set.difference` envFreeVars-                scheme = Forall genVars generalizedConstraints exprType-            -            -- Add to environment for subsequent bindings using Var directly-            modify $ \s -> s { inferEnv = extendEnv var scheme (inferEnv s) }-            -            return ((var, exprTI), subst)-  -  IPatternFunctionDecl name tyVars params retType body -> do-    -- Pattern function type checking:-    -- 1. Add parameters to environment for type checking-    -- 2. Infer body pattern with expected return type-    -- 3. Create type scheme with type parameters-    -    clearConstraints  -- Start fresh-    -    -- Add parameters to environment for type checking the body-    -- Note: Parameter types don't need Pattern wrapper (design/pattern.md)-    let paramBindings = map (\(pname, pty) -> (pname, Forall [] [] pty)) params-    withEnv paramBindings $ do-      -- Infer body pattern with expected return type-      let ctx = TypeErrorContext -                  { errorLocation = Nothing-                  , errorExpr = Just ("Pattern function: " ++ name)-                  , errorContext = Just ("Expected type: " ++ show retType)-                  }-      (tiBody, _bodyBindings, subst) <- inferIPattern body retType ctx-      -      -- Note: Pattern variables that reference parameters (using ~param) will appear in bodyBindings-      -- but they are NOT conflicts - they are references to the parameters themselves.-      -- Only NEW variable bindings (using $var) would be actual conflicts.-      -- Since the pattern body uses ~p1 and ~p2 (pattern variable references), -      -- not $p1 and $p2 (new bindings), we don't need to check for conflicts here.-      -- The existing semantics already handle this correctly during pattern matching.-      -      -- Create type scheme with type parameters-      -- Pattern function type: param1 -> param2 -> ... -> retType-      let paramTypes = map snd params-          funcType = foldr TFun retType paramTypes-          typeScheme = Forall tyVars [] funcType-      -      -- Add pattern function to both inferPatternFuncEnv and inferEnv-      -- This allows the type checker to recognize it in subsequent declarations-      modify $ \s -> s { -        inferPatternFuncEnv = extendPatternEnv name typeScheme (inferPatternFuncEnv s),-        inferEnv = extendEnv (stringToVar name) typeScheme (inferEnv s)-      }-      -      return (Just (TIPatternFunctionDecl name typeScheme params retType tiBody), subst)-  -  IDeclareSymbol names mType -> do-    -- Register declared symbols with their types-    let ty = case mType of-               Just t  -> t-               Nothing -> TInt  -- Default to Integer (MathExpr)+import           Control.Monad              (foldM, when, zipWithM)+import           Control.Monad.Except       (ExceptT, runExceptT, throwError, catchError)+import           Control.Monad.State.Strict (StateT, evalStateT, runStateT, get, gets, modify, put)+import           Data.List                  (isPrefixOf, nub, partition, intercalate)+import           Data.Maybe                  (catMaybes, fromMaybe)+import qualified Data.Map.Strict             as Map+import qualified Data.Set                    as Set+import           Language.Egison.AST        (ConstantExpr (..), PrimitivePatPattern (..))+import           Language.Egison.IExpr      (IExpr (..), ITopExpr (..), TITopExpr (..)+                                            , TIExpr (..), TIExprNode (..)+                                            , IBindingExpr, TIBindingExpr+                                            , IMatchClause, TIMatchClause, IPatternDef, TIPatternDef+                                            , IPattern (..), ILoopRange (..)+                                            , TIPattern (..), TIPatternNode (..), TILoopRange (..)+                                            , IPrimitiveDataPattern, PDPatternBase (..)+                                            , extractNameFromVar, Var (..), Index (..), stringToVar+                                            , tiExprType, mapTIExprChildren)+import           Language.Egison.Pretty     (prettyStr)+import           Language.Egison.Type.Env+import qualified Language.Egison.Type.Error as TE+import           Language.Egison.Type.Error (TypeError(..), TypeErrorContext(..), TypeWarning(..),+                                              emptyContext, withContext, withExpr)+import qualified Language.Egison.Type.Pretty as TP+import qualified Language.Egison.Type.Subtype as Subtype+import           Language.Egison.Type.Subst (Subst(..), applySubst, applySubstConstraint,+                                              applySubstScheme, composeSubst, emptySubst,+                                              singletonSubst)+import           Language.Egison.Type.Tensor (normalizeTensorType)+import           Language.Egison.Type.Types+import qualified Language.Egison.Type.Types as Types+import           Language.Egison.Type.Unify as TU+import qualified Language.Egison.Type.Unify as Unify+import           Language.Egison.Type.Instance (findMatchingInstanceForType)++--------------------------------------------------------------------------------+-- * Infer Monad and State+--------------------------------------------------------------------------------++-- | Inference configuration+data InferConfig = InferConfig+  { cfgPermissive       :: Bool  -- ^ Treat unbound variables as warnings, not errors+  , cfgCollectWarnings  :: Bool  -- ^ Collect warnings during inference+  , cfgMatcherConsistencyWarnings :: Bool  -- ^ Emit matcher consistency warnings (paper Def 4.2):+                                 --   Coverage (4.2(3)) and PP-Con (4.2(1a)).  Off by default, as+                                 --   the standard library has intentionally partial / non-strict+                                 --   matchers (opt-in diagnostic; --matcher-consistency-warnings).+                                 --   Arm exhaustiveness (4.2(1c)) is not gated here: it is an+                                 --   ordinary type error (see pdArmsExhaustive).+  }++instance Show InferConfig where+  show cfg = "InferConfig { cfgPermissive = " ++ show (cfgPermissive cfg)+           ++ ", cfgCollectWarnings = " ++ show (cfgCollectWarnings cfg)+           ++ ", cfgMatcherConsistencyWarnings = " ++ show (cfgMatcherConsistencyWarnings cfg)+           ++ " }"++-- | Default configuration (strict mode)+defaultInferConfig :: InferConfig+defaultInferConfig = InferConfig+  { cfgPermissive = False+  , cfgCollectWarnings = False+  , cfgMatcherConsistencyWarnings = False+  }++-- | Permissive configuration (for gradual adoption)+permissiveInferConfig :: InferConfig+permissiveInferConfig = InferConfig+  { cfgPermissive = True+  , cfgCollectWarnings = True+  , cfgMatcherConsistencyWarnings = False+  }++-- | Inference state+data InferState = InferState+  { inferCounter     :: Int              -- ^ Fresh variable counter+  , inferEnv         :: TypeEnv          -- ^ Current type environment+  , inferWarnings    :: [TypeWarning]    -- ^ Collected warnings+  , inferConfig      :: InferConfig      -- ^ Configuration+  , inferClassEnv    :: ClassEnv         -- ^ Type class environment+  , inferPatternEnv  :: PatternTypeEnv   -- ^ Pattern constructor environment (merged)+  , inferPatternFuncEnv :: PatternTypeEnv  -- ^ Pattern function environment (for disambiguation)+  , inferPatternFuncStructEnv :: PatternTypeEnv+                                          -- ^ Pattern function structural signatures (paper PATFUN-DEF):+                                          --   name |-> scheme of beta_1 -> ... -> beta_k -> tau_p_body,+                                          --   where beta_i is parameter i's structural index and+                                          --   tau_p_body the body's structural index.  PAT-APP+                                          --   instantiates this to compute an application's tau_p.+  , inferPatfunParamTaup :: Map.Map String Type+                                          -- ^ While inferring a pattern function body: parameter name+                                          --   |-> its structural index beta_i, consulted by IVarPat+                                          --   (the ~param embedding).  Empty outside such bodies.+  , inferPatfunTaupEqs :: Maybe [(Type, Type)]+                                          -- ^ While inferring a pattern function body (Just):+                                          --   the structural equations solved locally (and then+                                          --   discarded) by taupCombine / taupFromCtor.  The+                                          --   per-node solvers keep only each node's RESULT type,+                                          --   so links binding a parameter's beta_i can be lost to+                                          --   unifier direction; PATFUN-DEF re-solves the recorded+                                          --   equations jointly and applies the solution to the+                                          --   structural signature, recovering every link.+  , inferConstraints :: [Constraint]     -- ^ Accumulated type class constraints+  , declaredSymbols  :: Map.Map String Type  -- ^ Declared symbols with their types+  , inferInMatcherBody :: Bool           -- ^ True while inferring a `matcher` body.  The match-site+                                          --   admissibility check (T-MATCHALL) runs normally here —+                                          --   match-sites nested in a matcher body are genuinely+                                          --   type-checked.  This flag only suppresses matcher-+                                          --   Coverage (Def 4.2(3)) warnings for nested / generated+                                          --   matchers, whose constructor set is an implementation+                                          --   detail rather than a user-facing matcher.+  , inferDeferredHoleChecks :: [(Type, HoleCompShape, String, TypeErrorContext)]+                                          -- ^ Matcher-definition hole admissibility checks deferred+                                          --   to the end of the top-level expression (paper PP-Con,+                                          --   Def 4.2(1a)): the hole's TARGET type may be pinned only+                                          --   by the definition's annotation, so the structural test+                                          --   runs after the final substitution.  (holeTy, shape of+                                          --   the next-matcher component, error context).+  , inferGlobalSubst :: Subst             -- ^ The growing zonk substitution: every committed+                                          --   unification merges its result here, and the unify+                                          --   wrappers resolve both sides through it first.  Sibling+                                          --   subexpressions are inferred independently and their+                                          --   substitutions merged with the left-biased 'composeSubst',+                                          --   which on a conflicting binding silently keeps one side+                                          --   (e.g. two match sites committing the same lambda-bound+                                          --   matcher parameter to different slot types).  Zonking+                                          --   makes the later unification see the earlier commitment,+                                          --   so the conflict is unified — and reported — instead of+                                          --   shadowed.  Reset per top-level item (a fresh InferState+                                          --   is seeded for each).+  , inferCasSubtypeEdges :: Subtype.SubtypeEnv+                                          -- ^ Declared `cas-subtype` edges (alias-expanded), seeded+                                          --   from EvalState per top-level item.  Consulted by the+                                          --   application-site CAS join: when two CAS operand types+                                          --   fail to unify, their unique join in the declared order+                                          --   (D1) becomes the promotion target and both operands are+                                          --   reshaped to it (elaboration inserts the coercion; the+                                          --   unifier itself never joins).+  , inferBatchDefNames :: Set.Set String  -- ^ Names of the definitions of the current load unit,+                                          --   seeded per batch by Eval.  An unbound variable that is+                                          --   in this set is a FORWARD reference (were it defined+                                          --   earlier it would be in the environment), so the warning+                                          --   can say how to fix it instead of "unbound".+  } deriving (Show)++-- | Shape classification of a matcher-clause hole's next-matcher component,+-- recorded at clause-inference time (before the hole/target unification ties+-- its type to the hole) for the deferred admissibility check:+--   * HCSlot: a slot-typed parameter — committed to the hole by index+--     unification (Def 4.2(1a) parameter route), nothing left to check.+--   * HCBareVar: a bare-variable matcher value (eq / something) — admissible+--     only at a variable-headed or function-typed hole.+--   * HCShape t: a structured/concrete matcher value — its (freshened)+--     intrinsic inner type must one-way match the hole's structural index+--     (same head, fresh leaves).+data HoleCompShape = HCSlot | HCBareVar Type | HCShape Type+  deriving (Show)++-- | Initial inference state+initialInferState :: InferState+initialInferState = InferState 0 emptyEnv [] defaultInferConfig emptyClassEnv emptyPatternEnv emptyPatternEnv emptyPatternEnv Map.empty Nothing [] Map.empty False [] emptySubst [] Set.empty++-- | Create initial state with config+initialInferStateWithConfig :: InferConfig -> InferState+initialInferStateWithConfig cfg = InferState 0 emptyEnv [] cfg emptyClassEnv emptyPatternEnv emptyPatternEnv emptyPatternEnv Map.empty Nothing [] Map.empty False [] emptySubst [] Set.empty++-- | Inference monad (with IO for potential future extensions)+type Infer a = ExceptT TypeError (StateT InferState IO) a++-- | Run type inference+runInfer :: Infer a -> InferState -> IO (Either TypeError a)+runInfer m st = evalStateT (runExceptT m) st++-- | Run type inference and also return warnings+runInferWithWarnings :: Infer a -> InferState -> IO (Either TypeError a, [TypeWarning])+runInferWithWarnings m st = do+  (result, finalState) <- runStateT (runExceptT m) st+  return (result, inferWarnings finalState)++-- | Run inference and return result, warnings, and final state+runInferWithWarningsAndState :: Infer a -> InferState -> IO (Either TypeError a, [TypeWarning], InferState)+runInferWithWarningsAndState m st = do+  (result, finalState) <- runStateT (runExceptT m) st+  return (result, inferWarnings finalState, finalState)++--------------------------------------------------------------------------------+-- * Helper Functions+--------------------------------------------------------------------------------++-- | Add a warning+addWarning :: TypeWarning -> Infer ()+addWarning w = modify $ \st -> st { inferWarnings = w : inferWarnings st }++-- | The permissive-mode unbound-variable warning, upgraded to the+-- forward-reference variant when the name is a definition of the current+-- load unit: it must be defined LATER than this reference (an earlier+-- definition would already be in the environment), which has a concrete+-- fix (annotate it -- signatures are collected in a prepass).+warnUnboundVariable :: String -> TypeErrorContext -> Infer ()+warnUnboundVariable name ctx = do+  batchNames <- inferBatchDefNames <$> get+  if name `Set.member` batchNames+    then addWarning (ForwardReferenceWarning name ctx)+    else addWarning (UnboundVariableWarning name ctx)++-- | Clear all accumulated warnings+clearWarnings :: Infer ()+clearWarnings = modify $ \st -> st { inferWarnings = [] }++-- | Add type class constraints (with deduplication and superclass propagation)+-- When adding a constraint like "Ord a", this also adds superclass constraints+-- (e.g., "Eq a") recursively, so that superclass methods are available.+addConstraints :: [Constraint] -> Infer ()+addConstraints cs = do+  classEnv <- getClassEnv+  let expanded = expandSuperclasses classEnv cs+  modify $ \st ->+    let existing = inferConstraints st+        newConstraints = filter (`notElem` existing) expanded+    in st { inferConstraints = existing ++ newConstraints }++-- | Expand a list of constraints by recursively adding superclass constraints.+-- e.g., [Ord a] -> [Ord a, Eq a]  (since Ord extends Eq)+expandSuperclasses :: ClassEnv -> [Constraint] -> [Constraint]+expandSuperclasses classEnv = go []+  where+    go seen [] = seen+    go seen (c:rest)+      | c `elem` seen = go seen rest+      | otherwise =+          let supers = case lookupClass (constraintClass c) classEnv of+                Nothing -> []+                -- Superclasses inherit ALL the type arguments from the subclass+                -- (e.g. `class AddSemigroup a` superclassed by `class AddMonoid a`).+                -- This generalizes correctly to multi-param classes if the+                -- superclass has the same type-parameter arity (which is the+                -- common case in Egison).+                Just info -> map (\superName -> Constraint superName (constraintTypes c))+                                 (classSupers info)+          in go (seen ++ [c]) (supers ++ rest)++-- | Get accumulated constraints+getConstraints :: Infer [Constraint]+getConstraints = inferConstraints <$> get++-- | Clear accumulated constraints+clearConstraints :: Infer ()+clearConstraints = modify $ \st -> st { inferConstraints = [] }++-- | Check if we're in permissive mode+isPermissive :: Infer Bool+isPermissive = cfgPermissive . inferConfig <$> get++-- | Generate a fresh type variable+freshVar :: String -> Infer Type+freshVar prefix = do+  st <- get+  let n = inferCounter st+  put st { inferCounter = n + 1 }+  return $ TVar $ TyVar $ prefix ++ show n++-- | Get the current type environment+getEnv :: Infer TypeEnv+getEnv = inferEnv <$> get++-- | Set the type environment+setEnv :: TypeEnv -> Infer ()+setEnv env = modify $ \st -> st { inferEnv = env }++-- | Get the current pattern type environment+getPatternEnv :: Infer PatternTypeEnv+getPatternEnv = inferPatternEnv <$> get++-- | Get the current pattern function environment (for disambiguation)+getPatternFuncEnv :: Infer PatternTypeEnv+getPatternFuncEnv = inferPatternFuncEnv <$> get++-- | Get the pattern function structural-signature environment (paper PATFUN-DEF/PAT-APP)+getPatternFuncStructEnvI :: Infer PatternTypeEnv+getPatternFuncStructEnvI = inferPatternFuncStructEnv <$> get++-- | Get the current class environment+getClassEnv :: Infer ClassEnv+getClassEnv = inferClassEnv <$> get++-- | Resolve a constraint based on available instances+-- If the constraint type is a Tensor type and no instance exists for it,+-- try to use the element type's instance instead+-- | Resolve constraints in a TIExpr recursively+resolveConstraintsInTIExpr :: ClassEnv -> Subst -> TIExpr -> TIExpr+resolveConstraintsInTIExpr classEnv subst (TIExpr (Forall vars constraints ty) node) =+  let resolvedConstraints = map (resolveConstraintWithInstances classEnv subst) constraints+      resolvedNode = mapTIExprChildren (resolveConstraintsInTIExpr classEnv subst) node+  in TIExpr (Forall vars resolvedConstraints ty) resolvedNode++resolveConstraintWithInstances :: ClassEnv -> Subst -> Constraint -> Constraint+resolveConstraintWithInstances classEnv subst (Constraint className tyArgs) =+  let resolvedTypes = map (applySubst subst) tyArgs+      instances = lookupInstances className classEnv+      -- For multi-param constraints we apply Tensor unwrapping to the principal+      -- (first) type only; secondary types are passed through. This matches the+      -- existing semantics for single-param classes.+      resolvedFirst = case resolvedTypes of (t:_) -> t; [] -> TAny+      adjustFirst newFirst = case resolvedTypes of+                               (_:rest) -> newFirst : rest+                               []       -> [newFirst]+  in case resolvedFirst of+       TTensor elemType ->+         case findMatchingInstanceForType resolvedFirst instances of+           Just _  -> Constraint className resolvedTypes+           Nothing -> Constraint className (adjustFirst elemType)+       _ ->+         Constraint className resolvedTypes++-- | Strict signature-completeness check for annotated definitions.+-- Residual constraints (left in the inference state after checking the body)+-- that mention the signature's type variables must be entailed by the+-- (superclass-expanded) signature constraints; otherwise the signature is+-- missing a declaration the body relies on.  We reject such definitions+-- instead of silently emitting unresolvable dictionary references (the+-- declared signature is the contract; if the body needs {Ord a}, the+-- signature must say so).+checkResidualConstraints :: String -> [Constraint] -> Type -> Subst -> TypeErrorContext -> Infer ()+checkResidualConstraints defName sigConstraints finalType finalSubst ctx = do+  residual <- getConstraints+  classEnv <- getClassEnv+  let sigCs = map (applySubstConstraint finalSubst) sigConstraints+      sigVars = freeTyVars finalType+      hasVar c = any (not . Set.null . freeTyVars) (constraintTypes c)+      mentionsSig c =+        any (\t -> not (Set.null (freeTyVars t `Set.intersection` sigVars))) (constraintTypes c)+      entailed c = any (\sc -> constraintClass sc == constraintClass c+                            && constraintTypes sc == constraintTypes c) sigCs++      -- Reduce a constraint by instance resolution: an instance matching the+      -- constraint stands for its context, e.g. {Eq (Tensor t)} with+      -- `instance {Eq a} Eq (Tensor a)` reduces to {Eq t} (which the+      -- signature may then entail).  A Tensor type with no instance defers+      -- the constraint to its element type, mirroring+      -- resolveConstraintWithInstances.  Instance matching is one-way: the+      -- instance head's variables are bound, the constraint's types are+      -- rigid (TU.matchOneWay).+      reduceC :: Int -> Constraint -> [Constraint]+      reduceC 0 c = [c]+      reduceC d c@(Constraint cls tys)+        | entailed c = []+        | otherwise =+            let insts = lookupInstances cls classEnv+                matches = [ (inst, th)+                          | inst <- insts+                          , length (instTypes inst) == length tys+                          , Just th <- [matchTypesOneWay (instTypes inst) tys] ]+            in case matches of+                 ((inst, th) : _) ->+                   concatMap (reduceC (d - 1))+                             (map (applySubstConstraint th) (instContext inst))+                 [] -> case tys of+                   (TTensor el : restT) -> reduceC (d - 1) (Constraint cls (el : restT))+                   _ -> [c]+      matchTypesOneWay ps ts = foldM step emptySubst (zip ps ts)+        where step acc (p, t) = do+                s <- TU.matchOneWay (applySubst acc p) t+                return (composeSubst s acc)++      residual' = concatMap (reduceC 5 . applySubstConstraint finalSubst) residual+      missing = nub [ c | c <- residual', hasVar c, mentionsSig c, not (entailed c) ]+  when (not (null missing)) $+    throwError $ TE.MissingSignatureConstraint defName missing ctx++-- | Queue a matcher-definition hole admissibility check for the end of the+-- current top-level expression (see 'inferDeferredHoleChecks').+deferHoleCheck :: Type -> HoleCompShape -> String -> TypeErrorContext -> Infer ()+deferHoleCheck holeTy shape ppStr ctx =+  modify $ \s -> s { inferDeferredHoleChecks = (holeTy, shape, ppStr, ctx) : inferDeferredHoleChecks s }++-- | Drop all queued hole checks (called at the start of each top-level+-- expression, alongside clearConstraints).+clearDeferredHoleChecks :: Infer ()+clearDeferredHoleChecks = modify $ \s -> s { inferDeferredHoleChecks = [] }++-- | Run the queued matcher-hole admissibility checks against the final+-- substitution (paper PP-Con / Def 4.2(1a)).+flushDeferredHoleChecks :: Subst -> Infer ()+flushDeferredHoleChecks finalSubst = do+  checks <- gets inferDeferredHoleChecks+  clearDeferredHoleChecks+  classEnv <- getClassEnv+  mapM_ (runCheck classEnv) (reverse checks)+  where+    runCheck classEnv (holeTy0, shape, ppStr, ctx) = do+      holeTy <- applySubstWithConstraintsM finalSubst holeTy0+      case shape of+        HCSlot -> return ()+        HCBareVar compTy -> case holeTy of+          TVar _   -> return ()+          -- A function-typed hole admits a bare-variable matcher: function+          -- types can never own pattern constructors (the declaration+          -- grammar attaches them to type constructors only), so only+          -- value patterns / variables / wildcards can reach it.+          TFun _ _ -> return ()+          _ ->+                throwError $ TE.TypeMismatch+                  (TMatcherSlot holeTy holeTy)+                  compTy+                  ("the next matcher of clause `" ++ ppStr ++ "` is a bare-variable matcher, not structurally admissible at its hole's resolved type (paper PP-Con, Def 4.2(1a)); use a concrete matcher for that hole's type")+                  ctx+        HCShape inner0 -> case normalizeInductiveTypes (normalizeTensorType holeTy) of+          TVar _ -> return ()+          holeTyN -> do+            let inner = normalizeInductiveTypes (normalizeTensorType inner0)+            taup <- freshLeavesOf holeTyN+            -- The shape's variables are fresh copies (binding-independent),+            -- so a full unification realizes the one-way instance check while+            -- staying aware of the CAS tower (Term/Frac/Poly vs MathValue).+            case TU.unifyWithConstraints classEnv [] taup inner of+              Right _ -> return ()+              Left _  ->+                throwError $ TE.TypeMismatch+                  (TMatcherSlot holeTy holeTy)+                  (TMatcher inner)+                  ("the hole's next matcher is not structurally admissible at this hole (paper PP-Con, Def 4.2(1a)): its intrinsic type does not match the hole's structural index")+                  ctx++-- | The hole's structural index: same head as the (resolved) hole target+-- type, fresh leaves (paper PP-Con's fresh instantiation).+freshLeavesOf :: Type -> Infer Type+freshLeavesOf ty = case ty of+  TCollection _    -> TCollection <$> freshVar "leaf"+  TTuple ts        -> TTuple <$> mapM (const (freshVar "leaf")) ts+  TInductive n ts  -> TInductive n <$> mapM (const (freshVar "leaf")) ts+  TTensor _        -> TTensor <$> freshVar "leaf"+  THash _ _        -> THash <$> freshVar "leaf" <*> freshVar "leaf"+  _                -> return ty++-- | A copy of a type with all its free variables renamed fresh (binding+-- independence for the deferred structural check: the copy must not be+-- touched by the definition's ongoing unifications).+freshenTypeVars :: Type -> Infer Type+freshenTypeVars ty = do+  let vs = Set.toList (freeTyVars ty)+  pairs <- mapM (\v -> do { fv <- freshVar "fr"; return (v, fv) }) vs+  let s = foldr (\(v, fv) acc -> composeSubst (singletonSubst v fv) acc) emptySubst pairs+  return (applySubst s ty)++-- | Extend the environment temporarily+withEnv :: [(String, TypeScheme)] -> Infer a -> Infer a+withEnv bindings action = do+  oldEnv <- getEnv+  setEnv $ extendEnvMany (map (\(name, scheme) -> (stringToVar name, scheme)) bindings) oldEnv+  result <- action+  setEnv oldEnv+  return result++-- | Look up a variable's type+lookupVar :: String -> Infer Type+lookupVar name = do+  env <- getEnv+  case lookupEnv (stringToVar name) env of+    Just scheme -> do+      st <- get+      let (constraints, t, newCounter) = instantiate scheme (inferCounter st)+      -- Track constraints for type class resolution+      modify $ \s -> s { inferCounter = newCounter }+      addConstraints constraints+      return t+    Nothing -> do+      -- Check if this is a declared symbol+      st <- get+      case Map.lookup name (declaredSymbols st) of+        Just ty -> return ty  -- Return the declared type without warning+        Nothing -> do+          permissive <- isPermissive+          if permissive+            then do+              -- In permissive mode, treat as a warning and return a fresh type variable+              warnUnboundVariable name emptyContext+              freshVar "unbound"+            else throwError $ UnboundVariable name emptyContext++-- | Lookup variable and return type with constraints+lookupVarWithConstraints :: String -> Infer (Type, [Constraint])+lookupVarWithConstraints name = do+  env <- getEnv+  case lookupEnv (stringToVar name) env of+    Just scheme -> do+      st <- get+      let (constraints, t, newCounter) = instantiate scheme (inferCounter st)+      -- Track constraints for type class resolution+      modify $ \s -> s { inferCounter = newCounter }+      addConstraints constraints+      return (t, constraints)+    Nothing -> do+      -- Check if this is a declared symbol+      st <- get+      case Map.lookup name (declaredSymbols st) of+        Just ty -> return (ty, [])  -- Return the declared type without warning+        Nothing -> do+          permissive <- isPermissive+          if permissive+            then do+              -- In permissive mode, treat as a warning and return a fresh type variable+              warnUnboundVariable name emptyContext+              t <- freshVar "unbound"+              return (t, [])+            else throwError $ UnboundVariable name emptyContext++-- | Unify two types+unifyTypes :: Type -> Type -> Infer Subst+unifyTypes t1 t2 = unifyTypesWithContext t1 t2 emptyContext++-- | Unify two types with context information+-- This now uses the accumulated constraints from the Infer monad to properly+-- handle constraint-aware unification (e.g., ensuring {Num a} a doesn't unify with Tensor b)+-- | Error message for a matcher-rigidity violation (TU.MatcherRigidity): two+-- distinct Matcher types may not be unified (see the TMatcher/TMatcher case+-- in Type.Unify for the soundness argument).+matcherRigidityMsg :: String+matcherRigidityMsg =+  "matcher types are rigid: a matcher value's structural capability is fixed by its definition, "+  ++ "so two different Matcher types never unify (e.g. `something' cannot be specialized to a "+  ++ "concrete matcher type by context).  Pass the intended matcher directly; matcher-consuming "+  ++ "function parameters should be slot-typed (m : MatcherSlot a a)"++-- | Bind a fresh inner variable to a type's Matcher component.  Matcher types+-- are rigid (the TMatcher/TMatcher case of unifyG), so an already-Matcher type+-- is destructured directly -- binding only the fresh variable, a pure+-- extraction, not a semantic merge of two matcher types; any other type+-- (a yet-undetermined variable, a slot, a tuple of matchers) is constrained by+-- unification with @Matcher fresh@ as before.+bindMatcherInner :: TypeErrorContext -> Type -> Type -> Infer Subst+bindMatcherInner ctx ty freshInner = case ty of+  TMatcher inner -> unifyTypesWithContext freshInner inner ctx+  _              -> unifyTypesWithContext ty (TMatcher freshInner) ctx++-- | The expression under any lambda wrappers (the body a parameterized+-- definition is desugared to).  Also sees through the letrec produced by the+-- algebraicDataMatcher desugaring (a self-referencing matcher literal).+rhsCore :: IExpr -> IExpr+rhsCore (ILambdaExpr _ _ e) = rhsCore e+rhsCore (ILetRecExpr [(PDPatVar v, e@(IMatcherExpr _))] (IVarExpr name))+  | v == stringToVar name   = e+rhsCore e                   = e++-- | T-MATCHER checking mode: unify the inferred and declared types of an+-- annotated matcher-literal definition (possibly parameterized, i.e.+-- lambda-wrapped).  This is the one context where two Matcher types may have+-- their parameters unified: the literal's structural capability is derived by+-- the clause checks at the declared type -- the matcher value is being+-- DEFINED here, not re-typed -- so matcher rigidity does not apply at the+-- result position of the definition's type.  Parameter positions of the TFun+-- spine are unified normally (they are slots or ordinary types).+unifyMatcherDefType :: [Constraint] -> Type -> Type -> TypeErrorContext -> Infer Subst+unifyMatcherDefType cs (TFun a1 r1) (TFun a2 r2) ctx = do+  s1 <- unifyTypesWithConstraints cs a1 a2 ctx+  r1' <- applySubstWithConstraintsM s1 r1+  r2' <- applySubstWithConstraintsM s1 r2+  s2 <- unifyMatcherDefType (map (applySubstConstraint s1) cs) r1' r2' ctx+  return (composeSubst s2 s1)+unifyMatcherDefType cs (TMatcher t1) (TMatcher t2) ctx =+  unifyTypesWithConstraints cs t1 t2 ctx+unifyMatcherDefType cs t1 t2 ctx = unifyTypesWithConstraints cs t1 t2 ctx++unifyTypesWithContext :: Type -> Type -> TypeErrorContext -> Infer Subst+unifyTypesWithContext t1 t2 ctx = do+  constraints <- getConstraints+  classEnv <- getClassEnv+  -- Zonk both sides through the global substitution first: a variable already+  -- committed by a sibling subexpression (whose local substitution this caller+  -- never saw) resolves to its committed type, so a conflicting second+  -- commitment is unified against the first instead of silently shadowing it+  -- in a later left-biased 'composeSubst'.+  (t1', t2') <- zonkPair t1 t2+  case TU.unifyWithConstraints classEnv constraints t1' t2' of+    Right (s, _)  -> recordGlobalSubst s >> return s  -- Discard flag in basic unification+    Left err -> case err of+      TU.OccursCheck v t -> throwError $ OccursCheckError v t ctx+      TU.TypeMismatch a b -> throwError $ UnificationError a b ctx+      TU.MatcherRigidity a b -> throwError $ TE.TypeMismatch a b matcherRigidityMsg ctx++-- | Resolve both unification operands through 'inferGlobalSubst' (with the+-- usual constraint-aware Tensor adjustment).  'applySubstWithConstraintsM'+-- routes every application through the global substitution, so the empty+-- local substitution suffices here.+zonkPair :: Type -> Type -> Infer (Type, Type)+zonkPair t1 t2 = do+  t1' <- applySubstWithConstraintsM emptySubst t1+  t2' <- applySubstWithConstraintsM emptySubst t2+  return (t1', t2')++-- | Merge a committed unifier into the global zonk substitution.+recordGlobalSubst :: Subst -> Infer ()+recordGlobalSubst s =+  modify $ \st -> st { inferGlobalSubst = composeSubst s (inferGlobalSubst st) }++-- | Unify two types with context, allowing Tensor a to unify with a+-- This is used only for top-level definitions with type annotations+-- According to type-tensor-simple.md: "Only for top-level tensor definitions, if Tensor a is unified with a, it becomes a."+unifyTypesWithTopLevel :: Type -> Type -> TypeErrorContext -> Infer Subst+unifyTypesWithTopLevel t1 t2 ctx = do+  (t1', t2') <- zonkPair t1 t2+  case TU.unifyWithTopLevel t1' t2' of+    Right s  -> recordGlobalSubst s >> return s+    Left err -> case err of+      TU.OccursCheck v t -> throwError $ OccursCheckError v t ctx+      TU.TypeMismatch a b -> throwError $ UnificationError a b ctx+      TU.MatcherRigidity a b -> throwError $ TE.TypeMismatch a b matcherRigidityMsg ctx++-- | Unify two types with constraint-aware handling+-- This is crucial for unifying types when type variables have constraints+-- (e.g., {Num t0}) - the constraint affects how Tensor types are unified+unifyTypesWithConstraints :: [Constraint] -> Type -> Type -> TypeErrorContext -> Infer Subst+unifyTypesWithConstraints constraints t1 t2 ctx = do+  classEnv <- getClassEnv+  (t1', t2') <- zonkPair t1 t2+  case TU.unifyWithConstraints classEnv constraints t1' t2' of+    Right (s, _)  -> recordGlobalSubst s >> return s  -- Discard flag in basic unification+    Left err -> case err of+      TU.OccursCheck v t -> throwError $ OccursCheckError v t ctx+      TU.TypeMismatch a b -> throwError $ UnificationError a b ctx+      TU.MatcherRigidity a b -> throwError $ TE.TypeMismatch a b matcherRigidityMsg ctx++-- | Infer type for constants+inferConstant :: ConstantExpr -> Infer Type+inferConstant c = case c of+  CharExpr _    -> return TChar+  StringExpr _  -> return TString+  BoolExpr _    -> return TBool+  IntegerExpr _ -> return TInt+  FloatExpr _   -> return TFloat+  -- something : Matcher a (polymorphic matcher that matches any type)+  SomethingExpr -> do+    elemType <- freshVar "a"+    return (TMatcher elemType)+  -- undefined has a fresh type variable (bottom-like, can be any type)+  UndefinedExpr -> freshVar "undefined"++--------------------------------------------------------------------------------+-- * Type Inference for IExpr+--------------------------------------------------------------------------------++-- | Helper: Create a TIExpr with a simple monomorphic type (no type variables, no constraints)+mkTIExpr :: Type -> TIExprNode -> TIExpr+mkTIExpr ty node = TIExpr (Forall [] [] ty) node++-- | Simplify Tensor constraints in type schemes+-- Rewrites C (Tensor a) to C a when C (Tensor a) has no instance but C a does+-- This enables correct type class expansion for higher-order functions with Tensor arguments+simplifyTensorConstraints :: ClassEnv -> [Constraint] -> [Constraint]+simplifyTensorConstraints classEnv = map simplifyConstraint+  where+    hasInstance :: String -> Type -> Bool+    hasInstance cls ty =+      case findMatchingInstanceForType ty (lookupInstances cls classEnv) of+        Just _  -> True+        Nothing -> False+    +    simplifyConstraint :: Constraint -> Constraint+    simplifyConstraint (Constraint cls tys) = Constraint cls (map (unwrapTensorInType cls) tys)+      where+        unwrapTensorInType :: String -> Type -> Type+        unwrapTensorInType cls' ty0 = case ty0 of+          TTensor inner+            | hasInstance cls' ty0   -> ty0           -- Tensor has instance, keep it+            | hasInstance cls' inner -> unwrapTensorInType cls' inner  -- Unwrap recursively+            | otherwise              -> ty0           -- No instance for either, keep original+          _ -> ty0++-- | Apply a substitution to a type scheme with class environment awareness+-- This adjusts the substitution based on type class constraints:+-- When {Num t0} t0 -> t0 is unified with Tensor t1, if Num (Tensor t1) has no instance,+-- the substitution is adjusted to t0 -> t1 (unwrapping the Tensor)+applySubstSchemeWithClassEnv :: ClassEnv -> Subst -> TypeScheme -> TypeScheme+applySubstSchemeWithClassEnv classEnv (Subst m) (Forall vs cs t) =+  let m' = foldr Map.delete m vs+      -- Adjust substitution based on constraints+      m'' = adjustSubstForConstraints classEnv cs m'+      s' = Subst m''+  in Forall vs (map (applySubstConstraint s') cs) (applySubst s' t)+  where+    -- Adjust substitution to unwrap Tensor when constraint has no instance+    adjustSubstForConstraints :: ClassEnv -> [Constraint] -> Map.Map TyVar Type -> Map.Map TyVar Type+    adjustSubstForConstraints env constraints substMap =+      -- For each constraint, check if we need to adjust substitutions+      foldr (adjustForConstraint env substMap) substMap constraints++    adjustForConstraint :: ClassEnv -> Map.Map TyVar Type -> Constraint -> Map.Map TyVar Type -> Map.Map TyVar Type+    adjustForConstraint env originalSubst (Constraint cls constraintTys) currentSubst =+      -- Get all type variables across all constraint types (multi-param-friendly).+      let constraintVars = Set.toList $ Set.unions (map freeTyVars constraintTys)+      in foldr (adjustVarForClass env cls originalSubst) currentSubst constraintVars++    adjustVarForClass :: ClassEnv -> String -> Map.Map TyVar Type -> TyVar -> Map.Map TyVar Type -> Map.Map TyVar Type+    adjustVarForClass env cls originalSubst var currentSubst =+      case Map.lookup var originalSubst of+        Just replacementType@(TTensor _) ->+          -- This variable is being replaced with a Tensor type+          -- Check if the class has an instance for the Tensor type+          let instances = lookupInstances cls env+              hasTensorInstance = case findMatchingInstanceForType replacementType instances of+                                    Just _  -> True+                                    Nothing -> False+          in if hasTensorInstance+               then currentSubst  -- Keep the Tensor substitution+               else Map.insert var (unwrapTensorCompletely replacementType) currentSubst  -- Unwrap Tensor+        _ -> currentSubst  -- Not a Tensor substitution, keep as is++    -- Recursively unwrap Tensor to get the innermost type+    unwrapTensorCompletely :: Type -> Type+    unwrapTensorCompletely (TTensor inner) = unwrapTensorCompletely inner+    unwrapTensorCompletely ty = ty++-- | Apply a substitution to a TIExpr with ClassEnv awareness+-- This adjusts the substitution based on type class constraints+-- Example: {Num t0} t0 -> t0 with substitution t0 -> Tensor t1+--   If Num (Tensor t1) has no instance, the substitution is adjusted to t0 -> t1+applySubstToTIExprWithClassEnv :: ClassEnv -> Subst -> TIExpr -> TIExpr+applySubstToTIExprWithClassEnv classEnv s (TIExpr scheme node) =+  let updatedScheme = applySubstSchemeWithClassEnv classEnv s scheme+      updatedNode = applySubstToTIExprNodeWithClassEnv classEnv s node+  in TIExpr updatedScheme updatedNode++-- | Monadic version that uses ClassEnv to adjust substitutions based on constraints+-- Use this in type inference when you need to apply substitutions with constraint awareness+applySubstToTIExprM :: Subst -> TIExpr -> Infer TIExpr+applySubstToTIExprM s tiExpr = do+  classEnv <- getClassEnv+  g <- gets inferGlobalSubst+  -- Resolve through the global zonk substitution as well (see+  -- 'applySubstWithConstraintsM'): stored node schemes must not keep stale+  -- type variables that the global substitution has already committed.+  return $ applySubstToTIExprWithClassEnv classEnv (composeSubst g s) tiExpr++-- | Apply a substitution to a Type with constraint awareness+-- This is a monadic version that retrieves ClassEnv and constraints from the Infer monad+-- and adjusts the substitution based on type class constraints before applying it+applySubstWithConstraintsM :: Subst -> Type -> Infer Type+applySubstWithConstraintsM (Subst m) t = do+  classEnv <- getClassEnv+  constraints <- gets inferConstraints+  Subst gm <- gets inferGlobalSubst+  -- Adjust substitution based on constraints using the same logic as applySubstSchemeWithClassEnv+  let m' = adjustSubstForConstraints classEnv constraints m+      s' = Subst m'+      -- Also resolve through the global zonk substitution: with zonking, each+      -- unifier is a delta relative to the global state, so a locally threaded+      -- substitution alone may leave already-committed variables unresolved+      -- (and code that case-analyzes the applied type would misread them).+      gm' = adjustSubstForConstraints classEnv constraints gm+  return $ applySubst (Subst gm') (applySubst s' t)+  where+    -- Adjust substitution to unwrap Tensor when constraint has no instance+    adjustSubstForConstraints :: ClassEnv -> [Constraint] -> Map.Map TyVar Type -> Map.Map TyVar Type+    adjustSubstForConstraints env cs substMap =+      foldr (adjustForConstraint env substMap) substMap cs++    adjustForConstraint :: ClassEnv -> Map.Map TyVar Type -> Constraint -> Map.Map TyVar Type -> Map.Map TyVar Type+    adjustForConstraint env originalSubst (Constraint cls constraintTys) currentSubst =+      let constraintVars = Set.toList $ Set.unions (map freeTyVars constraintTys)+      in foldr (adjustVarForClass env cls originalSubst) currentSubst constraintVars++    adjustVarForClass :: ClassEnv -> String -> Map.Map TyVar Type -> TyVar -> Map.Map TyVar Type -> Map.Map TyVar Type+    adjustVarForClass env cls originalSubst var currentSubst =+      case Map.lookup var originalSubst of+        Just replacementType@(TTensor _) ->+          let instances = lookupInstances cls env+              hasTensorInstance = case findMatchingInstanceForType replacementType instances of+                                    Just _  -> True+                                    Nothing -> False+          in if hasTensorInstance+               then currentSubst+               else Map.insert var (unwrapTensorCompletely replacementType) currentSubst+        _ -> currentSubst++    unwrapTensorCompletely :: Type -> Type+    unwrapTensorCompletely (TTensor inner) = unwrapTensorCompletely inner+    unwrapTensorCompletely ty = ty++-- | Apply a substitution to a TIExprNode recursively with ClassEnv awareness+applySubstToTIExprNodeWithClassEnv :: ClassEnv -> Subst -> TIExprNode -> TIExprNode+applySubstToTIExprNodeWithClassEnv env s node = case node of+  TIConstantExpr c -> TIConstantExpr c+  TIVarExpr name -> TIVarExpr name++  TILambdaExpr mVar params body ->+    TILambdaExpr mVar params (applySubstToTIExprWithClassEnv env s body)++  TIApplyExpr func args ->+    TIApplyExpr (applySubstToTIExprWithClassEnv env s func) (map (applySubstToTIExprWithClassEnv env s) args)++  TITupleExpr exprs ->+    TITupleExpr (map (applySubstToTIExprWithClassEnv env s) exprs)++  TICollectionExpr exprs ->+    TICollectionExpr (map (applySubstToTIExprWithClassEnv env s) exprs)++  TIConsExpr h t ->+    TIConsExpr (applySubstToTIExprWithClassEnv env s h) (applySubstToTIExprWithClassEnv env s t)++  TIJoinExpr l r ->+    TIJoinExpr (applySubstToTIExprWithClassEnv env s l) (applySubstToTIExprWithClassEnv env s r)++  TIIfExpr cond thenE elseE ->+    TIIfExpr (applySubstToTIExprWithClassEnv env s cond) (applySubstToTIExprWithClassEnv env s thenE) (applySubstToTIExprWithClassEnv env s elseE)++  TILetExpr bindings body ->+    TILetExpr (map (\(pat, expr) -> (pat, applySubstToTIExprWithClassEnv env s expr)) bindings)+              (applySubstToTIExprWithClassEnv env s body)++  TILetRecExpr bindings body ->+    TILetRecExpr (map (\(pat, expr) -> (pat, applySubstToTIExprWithClassEnv env s expr)) bindings)+                 (applySubstToTIExprWithClassEnv env s body)++  TISeqExpr e1 e2 ->+    TISeqExpr (applySubstToTIExprWithClassEnv env s e1) (applySubstToTIExprWithClassEnv env s e2)++  TIInductiveDataExpr name exprs ->+    TIInductiveDataExpr name (map (applySubstToTIExprWithClassEnv env s) exprs)++  TIMatcherExpr patDefs ->+    -- Substitute in the data-clause arm bodies too, not just the next-matcher+    -- expression: arm bodies contain ordinary expressions (e.g. class-method+    -- calls) whose node schemes must see the final substitution, otherwise+    -- TypeClassExpand later sees stale type variables in their constraints+    -- and emits unbound dictionary references (the method name then leaks+    -- into evaluation as a string index).+    TIMatcherExpr (map (\(pat, expr, bindings) ->+      (pat, applySubstToTIExprWithClassEnv env s expr,+       map (\(dp, e) -> (dp, applySubstToTIExprWithClassEnv env s e)) bindings)) patDefs)++  TIMatchExpr mode target matcher clauses ->+    TIMatchExpr mode+                (applySubstToTIExprWithClassEnv env s target)+                (applySubstToTIExprWithClassEnv env s matcher)+                (map (\(pat, body) -> (pat, applySubstToTIExprWithClassEnv env s body)) clauses)++  TIMatchAllExpr mode target matcher clauses ->+    TIMatchAllExpr mode+                   (applySubstToTIExprWithClassEnv env s target)+                   (applySubstToTIExprWithClassEnv env s matcher)+                   (map (\(pat, body) -> (pat, applySubstToTIExprWithClassEnv env s body)) clauses)++  TIMemoizedLambdaExpr params body ->+    TIMemoizedLambdaExpr params (applySubstToTIExprWithClassEnv env s body)++  TIDoExpr bindings body ->+    TIDoExpr (map (\(pat, expr) -> (pat, applySubstToTIExprWithClassEnv env s expr)) bindings)+             (applySubstToTIExprWithClassEnv env s body)++  TICambdaExpr var body ->+    TICambdaExpr var (applySubstToTIExprWithClassEnv env s body)++  TIWithSymbolsExpr syms body ->+    TIWithSymbolsExpr syms (applySubstToTIExprWithClassEnv env s body)++  TIQuoteExpr e ->+    TIQuoteExpr (applySubstToTIExprWithClassEnv env s e)++  TIQuoteSymbolExpr e ->+    TIQuoteSymbolExpr (applySubstToTIExprWithClassEnv env s e)++  TIIndexedExpr override base indices ->+    TIIndexedExpr override (applySubstToTIExprWithClassEnv env s base) (fmap (applySubstToTIExprWithClassEnv env s) <$> indices)++  TISubrefsExpr override base ref ->+    TISubrefsExpr override (applySubstToTIExprWithClassEnv env s base) (applySubstToTIExprWithClassEnv env s ref)++  TISuprefsExpr override base ref ->+    TISuprefsExpr override (applySubstToTIExprWithClassEnv env s base) (applySubstToTIExprWithClassEnv env s ref)++  TIUserrefsExpr override base ref ->+    TIUserrefsExpr override (applySubstToTIExprWithClassEnv env s base) (applySubstToTIExprWithClassEnv env s ref)++  TIWedgeApplyExpr func args ->+    TIWedgeApplyExpr (applySubstToTIExprWithClassEnv env s func) (map (applySubstToTIExprWithClassEnv env s) args)++  TIFunctionExpr names ->+    TIFunctionExpr names++  TIVectorExpr exprs ->+    TIVectorExpr (map (applySubstToTIExprWithClassEnv env s) exprs)++  TIHashExpr pairs ->+    TIHashExpr (map (\(k, v) -> (applySubstToTIExprWithClassEnv env s k, applySubstToTIExprWithClassEnv env s v)) pairs)++  TIGenerateTensorExpr func shape ->+    TIGenerateTensorExpr (applySubstToTIExprWithClassEnv env s func) (applySubstToTIExprWithClassEnv env s shape)++  TITensorExpr shape elems ->+    TITensorExpr (applySubstToTIExprWithClassEnv env s shape) (applySubstToTIExprWithClassEnv env s elems)++  TITransposeExpr perm tensor ->+    TITransposeExpr (applySubstToTIExprWithClassEnv env s perm) (applySubstToTIExprWithClassEnv env s tensor)++  TIFlipIndicesExpr tensor ->+    TIFlipIndicesExpr (applySubstToTIExprWithClassEnv env s tensor)++  TITensorMapExpr func tensor ->+    TITensorMapExpr (applySubstToTIExprWithClassEnv env s func) (applySubstToTIExprWithClassEnv env s tensor)++  TITensorMap2Expr func t1 t2 ->+    TITensorMap2Expr (applySubstToTIExprWithClassEnv env s func) (applySubstToTIExprWithClassEnv env s t1) (applySubstToTIExprWithClassEnv env s t2)++  TITensorContractExpr tensor ->+    TITensorContractExpr (applySubstToTIExprWithClassEnv env s tensor)++  TIRuntimeDispatch className methodName candidates args ->+    TIRuntimeDispatch className methodName candidates (map (applySubstToTIExprWithClassEnv env s) args)++  TIReshape ty inner ->+    TIReshape (applySubst s ty) (applySubstToTIExprWithClassEnv env s inner)++-- | Infer type for IExpr+-- NEW: Returns TIExpr (typed expression) instead of (IExpr, Type, Subst)+-- This builds the recursive TIExpr structure directly during type inference+inferIExpr :: IExpr -> Infer (TIExpr, Subst)+inferIExpr expr = inferIExprWithContext expr emptyContext++-- | Infer type for IExpr with context information+-- NEW: Returns TIExpr (typed expression) with type information embedded+inferIExprWithContext :: IExpr -> TypeErrorContext -> Infer (TIExpr, Subst)+inferIExprWithContext expr ctx = case expr of+  -- Constants+  IConstantExpr c -> do+    ty <- inferConstant c+    let scheme = Forall [] [] ty+    return (TIExpr scheme (TIConstantExpr c), emptySubst)+  +  -- Variables+  IVarExpr name -> do+    -- Variables starting with ":::" are treated as Any type without warning+    if ":::" `isPrefixOf` name+      then do+        let scheme = Forall [] [] TAny+        return (TIExpr scheme (TIVarExpr name), emptySubst)+      else do+        (ty, constraints) <- lookupVarWithConstraints name+        let scheme = Forall [] constraints ty+        return (TIExpr scheme (TIVarExpr name), emptySubst)+  +  -- Tuples+  ITupleExpr elems -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    case elems of+      [] -> do+        -- Empty tuple: unit type ()+        let scheme = Forall [] [] (TTuple [])+        return (TIExpr scheme (TITupleExpr []), emptySubst)+      [single] -> do+        -- Single element tuple: same as the element itself (parentheses are just grouping)+        inferIExprWithContext single exprCtx+      _ -> do+        results <- mapM (\e -> inferIExprWithContext e exprCtx) elems+        let elemTIExprs = map fst results+            elemTypes = map (tiExprType . fst) results+            s = foldr composeSubst emptySubst (map snd results)+        +        -- Check if all elements are Matcher types+        -- If so, return Matcher (Tuple ...) instead of (Matcher ..., Matcher ...)+        appliedElemTypes <- mapM (applySubstWithConstraintsM s) elemTypes+        let matcherTypes = catMaybes (map extractMatcherType appliedElemTypes)+        +        if length matcherTypes == length appliedElemTypes && not (null appliedElemTypes)+          then do+            -- All elements are matchers: return Matcher (Tuple ...)+            let tupleType = TTuple matcherTypes+                resultType = TMatcher tupleType+                scheme = Forall [] [] resultType+            return (TIExpr scheme (TITupleExpr elemTIExprs), s)+          else do+            -- Not all elements are matchers: return regular tuple+            let resultType = TTuple appliedElemTypes+                scheme = Forall [] [] resultType+            return (TIExpr scheme (TITupleExpr elemTIExprs), s)+        where+          -- Extract the inner type from Matcher a -> Just a, otherwise Nothing+          extractMatcherType :: Type -> Maybe Type+          extractMatcherType (TMatcher t) = Just t+          extractMatcherType _ = Nothing+  +  -- Collections (Lists)+  ICollectionExpr elems -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    elemType <- freshVar "elem"+    (elemTIExprs, s) <- foldM (inferListElem elemType exprCtx) ([], emptySubst) elems+    elemType' <- applySubstWithConstraintsM s elemType+    let resultType = TCollection elemType'+    return (mkTIExpr resultType (TICollectionExpr (reverse elemTIExprs)), s)+    where+      inferListElem eType exprCtx (accExprs, s) e = do+        (tiExpr, s') <- inferIExprWithContext e exprCtx+        let t = tiExprType tiExpr+        eType' <- applySubstWithConstraintsM s eType+        s'' <- unifyTypesWithContext eType' t exprCtx+        return (tiExpr : accExprs, composeSubst s'' (composeSubst s' s))++  -- Cons+  IConsExpr headExpr tailExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (headTI, s1) <- inferIExprWithContext headExpr exprCtx+    (tailTI, s2) <- inferIExprWithContext tailExpr exprCtx+    let headType = tiExprType headTI+        tailType = tiExprType tailTI+        s12 = composeSubst s2 s1+    headType' <- applySubstWithConstraintsM s12 headType+    tailType' <- applySubstWithConstraintsM s12 tailType+    s3 <- unifyTypesWithContext (TCollection headType') tailType' exprCtx+    let finalS = composeSubst s3 s12+    resultType <- applySubstWithConstraintsM finalS tailType+    return (mkTIExpr resultType (TIConsExpr headTI tailTI), finalS)+  +  -- Join (list concatenation)+  IJoinExpr leftExpr rightExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (leftTI, s1) <- inferIExprWithContext leftExpr exprCtx+    (rightTI, s2) <- inferIExprWithContext rightExpr exprCtx+    let leftType = tiExprType leftTI+        rightType = tiExprType rightTI+        s12 = composeSubst s2 s1+    leftType' <- applySubstWithConstraintsM s12 leftType+    rightType' <- applySubstWithConstraintsM s12 rightType+    s3 <- unifyTypesWithContext leftType' rightType' exprCtx+    let finalS = composeSubst s3 s12+    resultType <- applySubstWithConstraintsM finalS leftType+    return (mkTIExpr resultType (TIJoinExpr leftTI rightTI), finalS)+  +  -- Hash (Map)+  IHashExpr pairs -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    keyType <- freshVar "hashKey"+    valType <- freshVar "hashVal"+    (pairTIs, s) <- foldM (inferHashPair keyType valType exprCtx) ([], emptySubst) pairs+    keyType' <- applySubstWithConstraintsM s keyType+    valType' <- applySubstWithConstraintsM s valType+    let resultType = THash keyType' valType'+    return (mkTIExpr resultType (TIHashExpr (reverse pairTIs)), s)+    where+      inferHashPair kType vType exprCtx (accPairs, s') (k, v) = do+        (kTI, s1) <- inferIExprWithContext k exprCtx+        (vTI, s2) <- inferIExprWithContext v exprCtx+        let kt = tiExprType kTI+            vt = tiExprType vTI+        kType' <- applySubstWithConstraintsM (composeSubst s2 s1) kType+        s3 <- unifyTypesWithContext kType' kt exprCtx+        vType' <- applySubstWithConstraintsM (composeSubst s3 (composeSubst s2 s1)) vType+        s4 <- unifyTypesWithContext vType' vt exprCtx+        return ((kTI, vTI) : accPairs, foldr composeSubst s' [s4, s3, s2, s1])+  +  -- Vector (Tensor)+  IVectorExpr elems -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    elemType <- freshVar "vecElem"+    (elemTIs, s) <- foldM (inferListElem elemType exprCtx) ([], emptySubst) elems+    elemType' <- applySubstWithConstraintsM s elemType+    let resultType = normalizeTensorType (TTensor elemType')+    return (mkTIExpr resultType (TIVectorExpr (reverse elemTIs)), s)+    where+      inferListElem eType exprCtx (accExprs, s) e = do+        (tiExpr, s') <- inferIExprWithContext e exprCtx+        let t = tiExprType tiExpr+        eType' <- applySubstWithConstraintsM s eType+        s'' <- unifyTypesWithContext eType' t exprCtx+        return (tiExpr : accExprs, composeSubst s'' (composeSubst s' s))++  -- Lambda+  ILambdaExpr mVar params body -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    argTypes <- mapM (\_ -> freshVar "arg") params+    let bindings = zipWith makeBinding params argTypes+    (bodyTIExpr, s) <- withEnv (map toScheme bindings) $ inferIExprWithContext body exprCtx+    let bodyType = tiExprType bodyTIExpr+    finalArgTypes <- mapM (applySubstWithConstraintsM s) argTypes+    let funType = foldr TFun bodyType finalArgTypes+    return (mkTIExpr funType (TILambdaExpr mVar params bodyTIExpr), s)+    where+      makeBinding var t = (extractNameFromVar var, t)+      toScheme (name, t) = (name, Forall [] [] t)+  +  -- Function Application+  IApplyExpr func args -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (funcTI, s1) <- inferIExprWithContext func exprCtx+    let funcType = tiExprType funcTI+    inferIApplicationWithContext funcTI funcType args s1 exprCtx++  -- Wedge apply expression (exterior product)+  IWedgeApplyExpr func args -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (funcTI, s1) <- inferIExprWithContext func exprCtx+    let funcType = tiExprType funcTI+    -- Wedge application is similar to normal application+    (resultTI, finalS) <- inferIApplicationWithContext funcTI funcType args s1 exprCtx+    -- Convert TIApplyExpr to TIWedgeApplyExpr to preserve wedge semantics+    let resultScheme = tiScheme resultTI+    case tiExprNode resultTI of+      TIApplyExpr funcTI' argTIs' ->+        return (TIExpr resultScheme (TIWedgeApplyExpr funcTI' argTIs'), finalS)+      _ -> return (resultTI, finalS)++  -- If expression+  IIfExpr cond thenExpr elseExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (condTI, s1) <- inferIExprWithContext cond exprCtx+    let condType = tiExprType condTI+    s2 <- unifyTypesWithContext condType TBool exprCtx+    let s12 = composeSubst s2 s1+    (thenTI, s3) <- inferIExprWithContext thenExpr exprCtx+    (elseTI, s4) <- inferIExprWithContext elseExpr exprCtx+    let thenType = tiExprType thenTI+        elseType = tiExprType elseTI+    thenType' <- applySubstWithConstraintsM s4 thenType+    s5 <- unifyTypesWithContext thenType' elseType exprCtx+    let finalS = foldr composeSubst emptySubst [s5, s4, s3, s12]+    resultType <- applySubstWithConstraintsM finalS elseType+    return (mkTIExpr resultType (TIIfExpr condTI thenTI elseTI), finalS)+  +  -- Let expression+  ILetExpr bindings body -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    env <- getEnv+    (bindingTIs, extendedEnv, s1) <- inferIBindingsWithContext bindings env emptySubst exprCtx+    (bodyTI, s2) <- withEnv extendedEnv $ inferIExprWithContext body exprCtx+    let bodyType = tiExprType bodyTI+        finalS = composeSubst s2 s1+    resultType <- applySubstWithConstraintsM finalS bodyType+    return (mkTIExpr resultType (TILetExpr bindingTIs bodyTI), finalS)+  +  -- LetRec expression+  ILetRecExpr bindings body -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    env <- getEnv+    (bindingTIs, extendedEnv, s1) <- inferIRecBindingsWithContext bindings env emptySubst exprCtx+    (bodyTI, s2) <- withEnv extendedEnv $ inferIExprWithContext body exprCtx+    let bodyType = tiExprType bodyTI+        finalS = composeSubst s2 s1+    resultType <- applySubstWithConstraintsM finalS bodyType+    return (mkTIExpr resultType (TILetRecExpr bindingTIs bodyTI), finalS)+  +  -- Sequence expression+  ISeqExpr expr1 expr2 -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (expr1TI, s1) <- inferIExprWithContext expr1 exprCtx+    (expr2TI, s2) <- inferIExprWithContext expr2 exprCtx+    let t2 = tiExprType expr2TI+    return (mkTIExpr t2 (TISeqExpr expr1TI expr2TI), composeSubst s2 s1)+  +  -- Inductive Data Constructor+  IInductiveDataExpr name args -> do+    -- Look up constructor type in environment+    env <- getEnv+    case lookupEnv (stringToVar name) env of+      Just scheme -> do+        -- Instantiate the type scheme+        st <- get+        let (_constraints, constructorType, newCounter) = instantiate scheme (inferCounter st)+        modify $ \s -> s { inferCounter = newCounter }+        -- Treat constructor as a function application+        inferIApplication name constructorType args emptySubst+      Nothing -> do+        -- Constructor not found in environment+        let exprCtx = withExpr (prettyStr expr) ctx+        permissive <- isPermissive+        if permissive+          then do+            -- In permissive mode, treat as a warning and return a fresh type variable+            addWarning $ UnboundVariableWarning name exprCtx+            resultType <- freshVar "ctor"+            return (mkTIExpr resultType (TIInductiveDataExpr name []), emptySubst)+          else throwError $ UnboundVariable name exprCtx+  +  -- Matchers (return Matcher type)+  IMatcherExpr patDefs -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    -- Paper Def 4.2(2) (Reachability / catch-all): a matcher must contain a catch-all clause+    -- `$ as M with $tgt -> N` (its primitive-pattern pattern is a bare hole `$`), so that+    -- variable and wildcard patterns are handled (delegating, typically to `something`) rather+    -- than getting stuck.  (Coverage, Def 4.2(3), is not enforced — it needs the full set of+    -- pattern constructors for the matched type, which Egison does not require to be declared;+    -- see design/matcher-slot.md, Stage 4, gap C.)+    if any (\(pp, _, _) -> case pp of PPPatVar -> True; _ -> False) patDefs+      then return ()+      else throwError $ TE.TypeMismatch+             (TMatcher (TVar (TyVar "a")))+             (TMatcher (TVar (TyVar "a")))+             "a `matcher` must contain a catch-all clause `$ as <matcher> with $tgt -> ...` (e.g. `$ as something`) so that variable and wildcard patterns are handled"+             exprCtx+    -- Infer type of each pattern definition (matcher clause)+    -- Each clause has: (PrimitivePatPattern, nextMatcherExpr, [(primitiveDataPat, targetExpr)])+    -- Mark that we are inside a matcher body.  Match-sites nested here are still fully checked+    -- for admissibility (T-MATCHALL); this flag only suppresses matcher-Coverage warnings for+    -- the nested / generated matchers a body may build (see `inferInMatcherBody`).+    savedInMB <- gets inferInMatcherBody+    modify $ \st -> st { inferInMatcherBody = True }+    results <- mapM (inferPatternDef exprCtx) patDefs+    modify $ \st -> st { inferInMatcherBody = savedInMB }+    +    -- Collect TIPatternDefs and substitutions+    let tiPatDefs = map fst results+        substs = concatMap (snd . snd) results  -- Extract [Subst] from (TIPatternDef, (Type, [Subst]))+        finalSubst = foldr composeSubst emptySubst substs+    +    -- All clauses should agree on the matched type+    -- Unify all matched types from each pattern definition+    matchedTypes <- mapM (\(_, (ty, _)) -> applySubstWithConstraintsM finalSubst ty) results+    (matchedTy, s_matched) <- case matchedTypes of+      [] -> do+        ty <- freshVar "matched"+        return (ty, emptySubst)+      (firstTy:restTys) -> do+        -- Unify all matched types+        s <- foldM (\accS ty -> do+            firstTy' <- applySubstWithConstraintsM accS firstTy+            ty' <- applySubstWithConstraintsM accS ty+            s' <- unifyTypesWithContext firstTy' ty' exprCtx+            return $ composeSubst s' accS+          ) emptySubst restTys+        resultTy <- applySubstWithConstraintsM s firstTy+        return (resultTy, s)+    +    let allSubst = composeSubst s_matched finalSubst+    -- Paper Def 4.2(3) Coverage (warning-level diagnostic, non-fatal): report any pattern+    -- constructor of the matched type that has no general clause `c $..$` (such a pattern+    -- would get stuck at runtime).  Coverage holds vacuously for a polymorphic matched type+    -- or one with no inductive pattern declaration (no constructors).  Suppressed inside a+    -- matcher body (generated / nested matchers).+    covOn <- cfgMatcherConsistencyWarnings <$> gets inferConfig+    inMB <- gets inferInMatcherBody+    matchedTyFinal <- applySubstWithConstraintsM allSubst matchedTy+    case (covOn && not inMB, matcherTypeHead matchedTyFinal) of+      (True, Just hd) -> do+        patEnv <- getPatternEnv+        let allCtors     = [ name | (name, sch) <- patternEnvToList patEnv, ctorResultHead sch == Just hd ]+            coveredCtors  = [ c | (pp, _, _) <- patDefs, Just c <- [generalClauseCtor pp] ]+            missing       = filter (`notElem` coveredCtors) allCtors+        if not (null allCtors) && not (null missing)+          then addWarning $ MatcherCoverageWarning matchedTyFinal missing exprCtx+          else return ()+      _ -> return ()+    -- Arm exhaustiveness (paper Def 4.2(1c), part of matcher consistency): once a clause's+    -- pp matches the pattern, the target is matched against that clause's data-pattern arms+    -- alone, and a miss there is a runtime failure ("Primitive data pattern match failed"),+    -- not a graceful backtrack — an arm that cannot decompose its target must say so by+    -- returning [].  Reject any clause whose arm set is not syntactically exhaustive (a+    -- conservative approximation of Def 4.2(1c); see pdArmsExhaustive); the standard-library+    -- convention of a final `| _ -> []` / `| $tgt -> ...` arm always passes.  Unlike the+    -- Coverage diagnostic this is an ordinary type error, not gated behind+    -- --matcher-consistency-warnings; it stays suppressed inside matcher bodies+    -- (generated / nested matchers), as for Coverage.+    when (not inMB) $+      mapM_ (\(pp, _, dataClauses) ->+               when (not (pdArmsExhaustive (map fst dataClauses))) $+                 throwError $ MatcherDataArmsNotExhaustive (prettyStr pp) matchedTyFinal exprCtx)+            patDefs+    return (mkTIExpr (TMatcher matchedTy) (TIMatcherExpr tiPatDefs), allSubst)+    where+      -- Infer a single pattern definition (matcher clause)+      -- Returns (TIPatternDef, (matched type, [substitutions]))+      inferPatternDef :: TypeErrorContext -> IPatternDef -> Infer (TIPatternDef, (Type, [Subst]))+      inferPatternDef ctx (ppPat, nextMatcherExpr, dataClauses) = do+        -- Infer the type of next matcher expression+        -- It should be a Matcher type (possibly Matcher of tuple, like Matcher (a, b))+        -- Note: (integer, integer) is inferred as Matcher (Integer, Integer), not (Matcher Integer, Matcher Integer)+        (nextMatcherTI, s1) <- inferIExprWithContext nextMatcherExpr ctx+        let nextMatcherType = tiExprType nextMatcherTI+        +        -- nextMatcherType must be a Matcher type+        -- Constrain it to `Matcher inner` / extract its inner type.  Matcher+        -- types are rigid (the TMatcher/TMatcher case of unifyG), so an+        -- already-Matcher type is destructured directly -- binding only the+        -- fresh inner variable, a pure extraction, not a semantic merge of two+        -- matcher types -- instead of being unified with `Matcher fresh`.+        matcherInnerTy <- freshVar "matcherInner"+        nextMatcherType' <- applySubstWithConstraintsM s1 nextMatcherType+        s1' <- bindMatcherInner ctx nextMatcherType' matcherInnerTy+        nextMatcherType'' <- applySubstWithConstraintsM s1' nextMatcherType+        +        -- Infer PrimitivePatPattern type to get matched type, pattern hole types, and variable bindings+        (matchedType, patternHoleTypes, ppBindings, s_pp) <- inferPrimitivePatPattern ppPat ctx+        let s1'' = composeSubst s_pp s1'+        matchedType' <- applySubstWithConstraintsM s1'' matchedType+        let -- Apply substitution to variable bindings+            ppBindings' = [(var, applySubstScheme s1'' scheme) | (var, scheme) <- ppBindings]++        -- Apply substitution to pattern hole types (keep as inner types)+        patternHoleTypes' <- mapM (applySubstWithConstraintsM s1'') patternHoleTypes++        -- Extract inner type(s) from next matcher type+        -- If multiple pattern holes, combine them into a tuple to match ITupleExpr behavior+        nextMatcherInnerTypes <- extractInnerTypesFromMatcher nextMatcherType'' (length patternHoleTypes') ctx++        -- Paper PP-Con / COERCE-MATCHER-TO-SLOT (Def 4.2(1a)) — matcher-definition-time structural+        -- admissibility, a HARD ERROR.  In the paper each hole gives a pair (τ_p ▷ τ_t): the target+        -- τ_t is the declared argument type σ_l (= `holeTy` below) and the structural index τ_p is a+        -- *fresh instantiation* of σ_l (PP-Con's device — same head, fresh leaves; NO fusion+        -- τ_p = τ_t, NO separate fresh_rename, uniform with PAT-CON at a match site).  The next+        -- matcher is consumed at the slot @MatcherSlot τ_p σ_l@, whose structural half is the+        -- one-way match τ_m ⊑ τ_p: a bare-variable matcher fills only a variable-headed or+        -- function-typed hole (functions admit no pattern constructors); a constructor-/concrete-+        -- headed hole rejects it (the paper's `weird`: it cannot decompose the hole, so a pattern+        -- routed through it gets stuck).+        --+        -- The test runs in TWO STAGES, because a hole's target type may be resolved only by the+        -- enclosing definition's final substitution (e.g. the annotation `: Matcher [Integer]`+        -- pinning the matched variable):+        --   * EAGER (here): the literal `something` (T-SOME) at a hole whose type is already+        --     constructor-/concrete-headed — rejected immediately, with the clause in context.+        --     A slot-typed parameter (`m : MatcherSlot a a`) or a structured next matcher+        --     (`list m`/`multiset m`) is never literal `something`, so it is not flagged here;+        --     an undetermined parameter's inner is fixed by the target unification below+        --     (`checkPatternHoleConsistency`) and must not be rejected prematurely.+        --   * DEFERRED (recorded below, run by `flushDeferredHoleChecks` at the end of the+        --     top-level expression): the general check at the RESOLVED hole types — components+        --     classified as slot / bare-variable value / shaped value (`HoleCompShape`), the+        --     shaped case checked against PP-Con's fresh-leaves structural index.+        -- (Coverage, Def 4.2(3), stays an opt-in warning — partial matchers are intentional.)+        let comps = case nextMatcherExpr of { ITupleExpr es -> es; e -> [e] }+        mapM_ (\(holeTy, comp) -> case (holeTy, comp) of+                 (TVar _, _)                      -> return ()+                 (TFun _ _, _)                    -> return ()+                 (_, IConstantExpr SomethingExpr) ->+                   throwError $ TE.TypeMismatch+                     (TMatcherSlot holeTy holeTy)+                     (TMatcher (TVar (TyVar "a")))+                     ("the next matcher `" ++ prettyStr comp ++ "` is a bare-variable matcher, " +++                      "not structurally admissible at a constructor-headed hole (paper PP-Con, " +++                      "Def 4.2(1a)); use a concrete matcher for that hole's type")+                     ctx+                 _                                -> return ())+              (zip patternHoleTypes' comps)+        -- Deferred (post-annotation) admissibility: a hole's target type may+        -- be pinned only by the enclosing definition's final substitution+        -- (e.g. the annotation `: Matcher [Integer]` resolving the matched+        -- variable), so the structural check above can be vacuous (TVar) at+        -- this point and yet fail at the resolved type.  Record each+        -- constructor-/tuple-pp hole's (target type, next-matcher shape) and+        -- re-check at the end of the top-level expression+        -- (flushDeferredHoleChecks).  PP-Hole (a bare `$` pp, the catch-all)+        -- gives its hole a FRESH VARIABLE structural index regardless of the+        -- target type, so nothing is deferred for it.+        let compTIs = case tiExprNode nextMatcherTI of+              TITupleExpr es -> es+              _              -> [nextMatcherTI]+        case ppPat of+          PPPatVar -> return ()+          _ | length compTIs == length patternHoleTypes' -> do+                -- Classify each component from its own inferred (intrinsic)+                -- type — BEFORE the hole/target unification ties it to the+                -- hole — so a slot-typed parameter is recognized as a slot+                -- and a bare-variable matcher value as bare.+                compTys <- mapM (applySubstWithConstraintsM s1'' . tiExprType) compTIs+                mapM_ (\(holeTy, compTI, compTy) -> do+                  mshape <- case (tiExprNode compTI, compTy) of+                    (_, TMatcherSlot _ _) -> return (Just HCSlot)+                    -- A bare-variable matcher VALUE: the literal `something`,+                    -- or a variable referring to one (eq, a polymorphic+                    -- matcher alias).  Only these are classified bare — an+                    -- APPLICATION whose result type is still an unresolved+                    -- variable is not (its shape follows from its own+                    -- definition's checking) and is skipped below.+                    (TIConstantExpr SomethingExpr, _) ->+                      return (Just (HCBareVar compTy))+                    (TIVarExpr vname, TMatcher (TVar _)) -> do+                      envHere <- getEnv+                      case lookupEnv (stringToVar vname) envHere of+                        -- Only a GENERALIZED bare matcher value (its scheme+                        -- quantifies the Matcher parameter: eq, a polymorphic+                        -- alias) is bare.  A monomorphically bound variable+                        -- (a lambda parameter, whose type is still being+                        -- discovered and whose slot-ness is revealed by the+                        -- signature) is the parameter route — checked at its+                        -- call sites by COERCE-MATCHER-TO-SLOT, not here.+                        Just (Forall qs _ (TMatcher (TVar v)))+                          | v `elem` qs -> return (Just (HCBareVar compTy))+                        _ -> return Nothing+                    (_, TMatcher (TVar _)) -> return Nothing+                    (_, TMatcher inner) -> Just . HCShape <$> freshenTypeVars inner+                    _ -> return Nothing+                  mapM_ (\shape -> deferHoleCheck holeTy shape (prettyStr ppPat) ctx) mshape)+                  (zip3 patternHoleTypes' compTIs compTys)+            | otherwise -> return ()  -- a single matcher covering several holes: skip++        -- Unify pattern hole types (inner types) with next matcher inner types+        s_unify <- checkPatternHoleConsistency patternHoleTypes' nextMatcherInnerTypes ctx+        let s1''' = composeSubst s_unify s1''+        +        -- Infer the type of data clauses with pp variables in scope, building+        -- the typed arms (TIBindingExpr) in the SAME pass that checks them.+        -- A single inference per arm matters: the arm TIExprs stored in the+        -- TIMatcherExpr node and the constraints recorded in the inference+        -- state must come from the same instantiation, and the clause+        -- substitutions must be returned, so that the definition's final+        -- substitution can resolve the arm nodes' constraint variables+        -- (e.g. the `a` of `==`'s {Eq a}).  A separate re-inference used to+        -- leave orphaned constraint variables behind, and TypeClassExpand+        -- then emitted unbound dictionary references for the arm's method+        -- calls (the method name leaked into evaluation as a string).+        dataClauseResults <- withEnv ppBindings' $+          mapM (inferDataClauseWithCheck ctx nextMatcherInnerTypes matchedType') dataClauses+        let dataClauseTIs = map fst dataClauseResults+            s2 = foldr composeSubst emptySubst (map snd dataClauseResults)++        let tiPatDef = (ppPat, nextMatcherTI, dataClauseTIs)++        return (tiPatDef, (matchedType', [s1''', s2]))+      +      -- Infer PrimitivePatPattern type+      -- Returns (matched type, pattern hole types, variable bindings, substitution)+      -- Pattern hole types are the inner types (without TMatcher wrapper)+      -- The caller should wrap them with TMatcher when unifying with next matcher types+      -- Variable bindings are for PPValuePat variables (#$val)+      -- Note: Pattern hole types are determined by the pattern constructor, not by external context+      inferPrimitivePatPattern :: PrimitivePatPattern -> TypeErrorContext -> Infer (Type, [Type], [(String, TypeScheme)], Subst)+      inferPrimitivePatPattern ppPat ctx = case ppPat of+        PPWildCard -> do+          -- Wildcard pattern: no pattern holes, no bindings+          matchedTy <- freshVar "matched"+          return (matchedTy, [], [], emptySubst)+        +        PPPatVar -> do+          -- Pattern variable ($): one pattern hole, no binding+          -- Returns the matched type as the pattern hole type+          -- The caller will wrap it with TMatcher when unifying with next matcher type+          matchedTy <- freshVar "matched"+          return (matchedTy, [matchedTy], [], emptySubst)+        +        PPValuePat var -> do+          -- Value pattern (#$val): no pattern holes, binds variable to matched type+          matchedTy <- freshVar "matched"+          let binding = (var, Forall [] [] matchedTy)+          return (matchedTy, [], [binding], emptySubst)+        +        PPTuplePat ppPats -> do+          -- Tuple pattern: ($p1, $p2, ...)+          -- Recursively infer each sub-pattern+          results <- mapM (\pp -> inferPrimitivePatPattern pp ctx) ppPats+          let matchedTypes = [mt | (mt, _, _, _) <- results]+              patternHoleLists = [phs | (_, phs, _, _) <- results]+              bindingLists = [bs | (_, _, bs, _) <- results]+              substs = [s | (_, _, _, s) <- results]+              allPatternHoles = concat patternHoleLists+              allBindings = concat bindingLists+              finalSubst = foldr composeSubst emptySubst substs+          +          -- Matched type is tuple of matched types+          matchedTypes' <- mapM (applySubstWithConstraintsM finalSubst) matchedTypes+          allPatternHoles' <- mapM (applySubstWithConstraintsM finalSubst) allPatternHoles+          let matchedTy = TTuple matchedTypes'+          return (matchedTy, allPatternHoles', allBindings, finalSubst)+        +        PPInductivePat name ppPats -> do+          -- Inductive pattern: look up pattern constructor type from pattern environment+          patternEnv <- getPatternEnv+          case lookupPatternEnv name patternEnv of+            Just scheme -> do+              -- Found in pattern environment: use the declared type+              st <- get+              let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)+              modify $ \s -> s { inferCounter = newCounter }+              +              -- Pattern constructor type: arg1 -> arg2 -> ... -> resultType+              -- Extract argument types and result type+              let (argTypes, resultType) = extractFunctionArgs ctorType+              +              -- Check argument count matches+              if length argTypes /= length ppPats+                then throwError $ TE.TypeMismatch+                       (foldr TFun resultType (replicate (length ppPats) (TVar (TyVar "a"))))+                       ctorType+                       ("Pattern constructor " ++ name ++ " expects " ++ show (length argTypes) +                        ++ " arguments, but got " ++ show (length ppPats))+                       ctx+                else do+                  -- Recursively infer each sub-pattern+                  results <- mapM (\pp -> inferPrimitivePatPattern pp ctx) ppPats+                  +                  let matchedTypes = [mt | (mt, _, _, _) <- results]+                      patternHoleLists = [phs | (_, phs, _, _) <- results]+                      bindingLists = [bs | (_, _, bs, _) <- results]+                      substs = [s | (_, _, _, s) <- results]+                      allPatternHoles = concat patternHoleLists+                      allBindings = concat bindingLists+                      s = foldr composeSubst emptySubst substs+                  +                  -- Verify that inferred matched types match expected argument types+                  -- Extract inner types from Matcher types in argTypes+                  let expectedMatchedTypes = map (\ty -> case ty of+                        TMatcher inner -> inner+                        _ -> ty) argTypes+                  s' <- foldM (\accS (inferredTy, expectedTy) -> do+                      inferredTy' <- applySubstWithConstraintsM accS inferredTy+                      expectedTy' <- applySubstWithConstraintsM accS expectedTy+                      s'' <- unifyTypesWithContext inferredTy' expectedTy' ctx+                      return $ composeSubst s'' accS+                    ) s (zip matchedTypes expectedMatchedTypes)++                  resultType' <- applySubstWithConstraintsM s' resultType+                  allPatternHoles' <- mapM (applySubstWithConstraintsM s') allPatternHoles+                  return (resultType', allPatternHoles', allBindings, s')+            +            Nothing -> do+              -- Not found in pattern environment: use generic inference+              -- This is for backward compatibility+              results <- mapM (\pp -> inferPrimitivePatPattern pp ctx) ppPats+              let matchedTypes = [mt | (mt, _, _, _) <- results]+                  patternHoleLists = [phs | (_, phs, _, _) <- results]+                  bindingLists = [bs | (_, _, bs, _) <- results]+                  substs = [s | (_, _, _, s) <- results]+                  allPatternHoles = concat patternHoleLists+                  allBindings = concat bindingLists+                  s = foldr composeSubst emptySubst substs+              +              -- Result type is inductive type+              matchedTypes' <- mapM (applySubstWithConstraintsM s) matchedTypes+              allPatternHoles' <- mapM (applySubstWithConstraintsM s) allPatternHoles+              let resultType = TInductive name matchedTypes'+              return (resultType, allPatternHoles', allBindings, s)+      +      -- Extract function argument types and result type+      -- e.g., a -> b -> c -> d  =>  ([a, b, c], d)+      extractFunctionArgs :: Type -> ([Type], Type)+      extractFunctionArgs (TFun arg rest) = +        let (args, result) = extractFunctionArgs rest+        in (arg : args, result)+      extractFunctionArgs t = ([], t)+      +      -- Extract matched type from Matcher type+      -- Check consistency between pattern hole types and next matcher types+      checkPatternHoleConsistency :: [Type] -> [Type] -> TypeErrorContext -> Infer Subst+      checkPatternHoleConsistency [] [] _ctx = return emptySubst+      checkPatternHoleConsistency patternHoles nextMatchers ctx+        | length patternHoles /= length nextMatchers = +            throwError $ TE.TypeMismatch+              (TTuple nextMatchers)+              (TTuple patternHoles)+              ("Inconsistent number of pattern holes (" ++ show (length patternHoles) +               ++ ") and next matchers (" ++ show (length nextMatchers) ++ ")")+              ctx+        | otherwise = do+            -- Unify each pattern hole type with corresponding next matcher type+            foldM (\accS (holeTy, matcherTy) -> do+                holeTy' <- applySubstWithConstraintsM accS holeTy+                matcherTy' <- applySubstWithConstraintsM accS matcherTy+                s <- unifyTypesWithContext holeTy' matcherTy' ctx+                return $ composeSubst s accS+              ) emptySubst (zip patternHoles nextMatchers)+      +      -- Extract inner types from next matcher type+      -- Given Matcher a, returns [a]+      -- Given Matcher (a, b, ...) and n pattern holes, returns [a, b, ...] if n > 1, or [(a, b, ...)] if n = 1+      -- Special case: (Matcher a, Matcher b, ...) should be converted to Matcher (a, b, ...) first+      -- Note: Even when numHoles = 0, we extract inner types to detect mismatches in checkPatternHoleConsistency+      extractInnerTypesFromMatcher :: Type -> Int -> TypeErrorContext -> Infer [Type]+      extractInnerTypesFromMatcher matcherType numHoles ctx = case numHoles of+        0 -> case matcherType of+          -- No pattern holes, but extract inner type to allow error detection+          TMatcher innerType -> return [innerType]+          -- A MatcherSlot used as a next-matcher: its target component is the inner type.+          TMatcherSlot _ tt -> return [tt]+          TTuple types -> do+            let matcherInners = mapM extractMatcherInner types+            case matcherInners of+              Just inners -> return inners+              Nothing -> return []  -- Not matcher types, return empty+          _ -> return []  -- Not a matcher type+        1 -> case matcherType of+          TMatcher innerType -> return [innerType]  -- Single hole: return inner type as-is+          TMatcherSlot _ tt -> return [tt]          -- A slot next-matcher: target component+          -- Special case: (Matcher a, Matcher b, ...) from ITupleExpr that failed to convert+          -- This can happen when matcher parameters are used before ITupleExpr conversion+          TTuple types -> do+            let matcherInners = mapM extractMatcherInner types+            case matcherInners of+              Just inners -> return [TTuple inners]  -- Return as single tuple type+              Nothing -> throwError $ TE.TypeMismatch+                           (TMatcher (TVar (TyVar "a")))+                           matcherType+                           "Expected Matcher type or tuple of Matcher types"+                           ctx+          _ -> throwError $ TE.TypeMismatch+                 (TMatcher (TVar (TyVar "a")))+                 matcherType+                 "Expected Matcher type"+                 ctx+        n -> case matcherType of+          -- Multiple holes: expect Matcher (tuple) and extract each element+          TMatcher (TTuple innerTypes) ->+            if length innerTypes == n+              then return innerTypes+              else throwError $ TE.TypeMismatch+                     (TMatcher (TTuple (replicate n (TVar (TyVar "a")))))+                     matcherType+                     ("Expected Matcher with tuple of " ++ show n ++ " elements, but got " ++ show (length innerTypes))+                     ctx+          -- A product MatcherSlot used as a next-matcher: target tuple component.+          TMatcherSlot _ (TTuple innerTypes) ->+            if length innerTypes == n+              then return innerTypes+              else throwError $ TE.TypeMismatch+                     (TMatcher (TTuple (replicate n (TVar (TyVar "a")))))+                     matcherType+                     ("Expected Matcher with tuple of " ++ show n ++ " elements, but got " ++ show (length innerTypes))+                     ctx+          -- Special case: (Matcher a, Matcher b, ...) - extract inner types directly+          TTuple types -> do+            let matcherInners = mapM extractMatcherInner types+            case matcherInners of+              Just inners | length inners == n -> return inners+              _ -> throwError $ TE.TypeMismatch+                     (TMatcher (TTuple (replicate n (TVar (TyVar "a")))))+                     matcherType+                     "Expected tuple of Matcher types with correct count"+                     ctx+          _ -> throwError $ TE.TypeMismatch+                 (TMatcher (TTuple (replicate n (TVar (TyVar "a")))))+                 matcherType+                 ("Expected Matcher of tuple with " ++ show n ++ " elements")+                 ctx+      +      -- Helper: Extract inner type from Matcher a -> Just a, otherwise Nothing+      extractMatcherInner :: Type -> Maybe Type+      extractMatcherInner (TMatcher t) = Just t+      extractMatcherInner (TMatcherSlot _ tt) = Just tt+      extractMatcherInner _ = Nothing+      +      -- Infer a data clause with type checking+      -- Check that the target expression returns a list of values with types matching next matcher inner types+      -- Also uses matched type for validation+      -- nextMatcherInnerTypes: inner types extracted from next matcher (already without TMatcher wrapper)+      inferDataClauseWithCheck :: TypeErrorContext -> [Type] -> Type -> (IPrimitiveDataPattern, IExpr) -> Infer ((IPrimitiveDataPattern, TIExpr), Subst)+      inferDataClauseWithCheck ctx nextMatcherInnerTypes matchedType (pdPat, targetExpr) = do+        -- Extract expected element type from next matcher inner types (the target type)+        -- This is the type of elements in the list returned by the target expression+        targetType <- case nextMatcherInnerTypes of+          [] -> return (TTuple [])  -- No pattern holes: empty tuple () case+          [single] -> return single  -- Single pattern hole: use inner type directly+          multiple -> return (TTuple multiple)  -- Multiple holes: tuple of inner types+        +        -- Infer PrimitiveDataPattern with matched type+        -- Primitive data pattern matches against values of the matched type+        -- and produces bindings and next targets+        (pdTargetType, bindings, s_pd) <- inferPrimitiveDataPattern pdPat matchedType ctx+        +        -- The primitive data pattern should match the matched type+        -- No need to unify pdTargetType with targetType - they serve different purposes+        -- pdTargetType: type of data that pdPat matches (should be matchedType)+        -- targetType: type of next targets returned by the target expression+        +        -- Verify that pdTargetType is consistent with matchedType+        pdTargetType' <- applySubstWithConstraintsM s_pd pdTargetType+        matchedType' <- applySubstWithConstraintsM s_pd matchedType+        s_match <- unifyTypesWithContext pdTargetType' matchedType' ctx+        let s_pd' = composeSubst s_match s_pd++        -- Infer the target expression with pattern variables in scope+        (targetTI, s1) <- withEnv bindings $ inferIExprWithContext targetExpr ctx+        let exprType = tiExprType targetTI+            s_combined = composeSubst s1 s_pd'++        -- Unify with actual expression type+        -- Expected: [targetType]+        targetType' <- applySubstWithConstraintsM s_combined targetType+        let expectedType = TCollection targetType'++        exprType' <- applySubstWithConstraintsM s_combined exprType+        s2 <- unifyTypesWithContext exprType' expectedType ctx+        return ((pdPat, targetTI), composeSubst s2 s_combined)++      -- Helper to check if a pattern is a pattern variable+      isPDPatVar :: IPrimitiveDataPattern -> Bool+      isPDPatVar (PDPatVar _) = True+      isPDPatVar _ = False+      +      -- Infer PrimitiveDataPattern type+      -- Returns (inferred target type, variable bindings, substitution)+      -- This is similar to pattern matching in Haskell for algebraic data types+      inferPrimitiveDataPattern :: IPrimitiveDataPattern -> Type -> TypeErrorContext -> Infer (Type, [(String, TypeScheme)], Subst)+      inferPrimitiveDataPattern pdPat expectedType ctx = case pdPat of+        PDWildCard -> do+          -- Wildcard: matches any type, no bindings+          return (expectedType, [], emptySubst)+        +        PDPatVar var -> do+          -- Pattern variable: binds to the expected type+          let varName = extractNameFromVar var+          return (expectedType, [(varName, Forall [] [] expectedType)], emptySubst)+        +        PDConstantPat c -> do+          -- Constant pattern: must match the constant's type+          constTy <- inferConstant c+          s <- unifyTypesWithContext constTy expectedType ctx+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', [], s)+        +        PDTuplePat pats -> do+          -- Tuple pattern: expected type should be a tuple+          case expectedType of+            TTuple types | length types == length pats -> do+              -- Types match: infer each sub-pattern+              results <- zipWithM (\p t -> inferPrimitiveDataPattern p t ctx) pats types+              let (_, bindingsList, substs) = unzip3 results+                  allBindings = concat bindingsList+                  s = foldr composeSubst emptySubst substs+              expectedType' <- applySubstWithConstraintsM s expectedType+              return (expectedType', allBindings, s)+            +            TVar _ -> do+              -- Expected type is a type variable: create fresh types for each element+              elemTypes <- mapM (\_ -> freshVar "elem") pats+              let tupleTy = TTuple elemTypes+              s <- unifyTypesWithContext expectedType tupleTy ctx++              -- Recursively infer each sub-pattern+              elemTypes' <- mapM (applySubstWithConstraintsM s) elemTypes+              results <- zipWithM (\p t -> inferPrimitiveDataPattern p t ctx) pats elemTypes'+              let (_, bindingsList, substs) = unzip3 results+                  allBindings = concat bindingsList+                  s' = foldr composeSubst s substs+              tupleTy' <- applySubstWithConstraintsM s' tupleTy+              return (tupleTy', allBindings, s')+            +            _ -> do+              -- Type mismatch+              throwError $ TE.TypeMismatch+                (TTuple (replicate (length pats) (TVar (TyVar "a"))))+                expectedType+                "Tuple pattern but target is not a tuple type"+                ctx+        +        PDEmptyPat -> do+          -- Empty collection pattern: expected type should be [a] for some a+          elemTy <- freshVar "elem"+          s <- unifyTypesWithContext expectedType (TCollection elemTy) ctx+          collTy <- applySubstWithConstraintsM s (TCollection elemTy)+          return (collTy, [], s)+        +        PDConsPat p1 p2 -> do+          -- Cons pattern: expected type should be [a] for some a+          case expectedType of+            TCollection elemType -> do+              -- Infer head pattern with element type+              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 elemType ctx+              -- Infer tail pattern with collection type+              expectedType' <- applySubstWithConstraintsM s1 expectedType+              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 expectedType' ctx+              let s = composeSubst s2 s1+              expectedType'' <- applySubstWithConstraintsM s expectedType+              return (expectedType'', bindings1 ++ bindings2, s)+            +            TVar _ -> do+              -- Expected type is a type variable: constrain it to be a collection+              elemTy <- freshVar "elem"+              s <- unifyTypesWithContext expectedType (TCollection elemTy) ctx+              collTy <- applySubstWithConstraintsM s (TCollection elemTy)+              elemTy' <- applySubstWithConstraintsM s elemTy+              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 elemTy' ctx+              collTy' <- applySubstWithConstraintsM s1 collTy+              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 collTy' ctx+              let s' = composeSubst s2 (composeSubst s1 s)+              collTy'' <- applySubstWithConstraintsM s' collTy+              return (collTy'', bindings1 ++ bindings2, s')+            +            _ -> do+              throwError $ TE.TypeMismatch+                (TCollection (TVar (TyVar "a")))+                expectedType+                "Cons pattern but target is not a collection type"+                ctx+        +        PDSnocPat p1 p2 -> do+          -- Snoc pattern: similar to cons but reversed+          case expectedType of+            TCollection elemType -> do+              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 expectedType ctx+              elemType' <- applySubstWithConstraintsM s1 elemType+              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 elemType' ctx+              let s = composeSubst s2 s1+              expectedType' <- applySubstWithConstraintsM s expectedType+              return (expectedType', bindings1 ++ bindings2, s)+            +            TVar _ -> do+              elemTy <- freshVar "elem"+              s <- unifyTypesWithContext expectedType (TCollection elemTy) ctx+              collTy <- applySubstWithConstraintsM s (TCollection elemTy)+              elemTy' <- applySubstWithConstraintsM s elemTy+              (_, bindings1, s1) <- inferPrimitiveDataPattern p1 collTy ctx+              elemTy'' <- applySubstWithConstraintsM s1 elemTy'+              (_, bindings2, s2) <- inferPrimitiveDataPattern p2 elemTy'' ctx+              let s' = composeSubst s2 (composeSubst s1 s)+              collTy' <- applySubstWithConstraintsM s' collTy+              return (collTy', bindings1 ++ bindings2, s')+            +            _ -> do+              throwError $ TE.TypeMismatch+                (TCollection (TVar (TyVar "a")))+                expectedType+                "Snoc pattern but target is not a collection type"+                ctx+        +        PDInductivePat name pats -> do+          -- Inductive pattern: look up data constructor type from environment+          env <- getEnv+          case lookupEnv (stringToVar name) env of+            Just scheme -> do+              -- Found in environment: use the declared type+              st <- get+              let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)+              modify $ \s -> s { inferCounter = newCounter }+              +              -- Data constructor type: arg1 -> arg2 -> ... -> resultType+              let (argTypes, resultType) = extractFunctionArgs ctorType+              +              -- Check argument count matches+              if length argTypes /= length pats+                then throwError $ TE.TypeMismatch+                       (foldr TFun resultType (replicate (length pats) (TVar (TyVar "a"))))+                       ctorType+                       ("Data constructor " ++ name ++ " expects " ++ show (length argTypes) +                        ++ " arguments, but got " ++ show (length pats))+                       ctx+                else do+                  -- Unify result type with expected type+                  s0 <- unifyTypesWithContext resultType expectedType ctx+                  resultType' <- applySubstWithConstraintsM s0 resultType+                  argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes++                  -- Recursively infer each sub-pattern+                  results <- zipWithM (\p argTy -> inferPrimitiveDataPattern p argTy ctx) pats argTypes'+                  let (_, bindingsList, substs) = unzip3 results+                      allBindings = concat bindingsList+                      s = foldr composeSubst s0 substs++                  -- Return the result type, not expected type+                  resultType'' <- applySubstWithConstraintsM s resultType'+                  return (resultType'', allBindings, s)+            +            Nothing -> do+              -- Not found in environment: use generic inference+              argTypes <- mapM (\_ -> freshVar "arg") pats+              let resultType = TInductive name argTypes++              s0 <- unifyTypesWithContext resultType expectedType ctx+              resultType' <- applySubstWithConstraintsM s0 resultType++              argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes+              results <- zipWithM (\p argTy -> inferPrimitiveDataPattern p argTy ctx) pats argTypes'+              let (_, bindingsList, substs) = unzip3 results+                  allBindings = concat bindingsList+                  s = foldr composeSubst s0 substs++              resultType'' <- applySubstWithConstraintsM s resultType'+              return (resultType'', allBindings, s)+        +        -- MathValue primitive patterns+        PDFracPat patNum patDen -> do+          -- Div: MathValue -> PolyExpr, PolyExpr+          -- However, if pattern is a pattern variable, it gets MathValue (auto-conversion)+          let polyExprTy = TPolyExpr+              mathValueTy = TMathValue+              numTy = if isPDPatVar patNum then mathValueTy else polyExprTy+              denTy = if isPDPatVar patDen then mathValueTy else polyExprTy+          (_, bindings1, s1) <- inferPrimitiveDataPattern patNum numTy ctx+          denTy' <- applySubstWithConstraintsM s1 denTy+          (_, bindings2, s2) <- inferPrimitiveDataPattern patDen denTy' ctx+          let s = composeSubst s2 s1+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings1 ++ bindings2, s)+        +        PDPlusPat patTerms -> do+          -- Plus: PolyExpr -> [TermExpr]+          -- If pattern variable, it gets [MathValue]+          let termExprTy = TTermExpr+              mathValueTy = TMathValue+              termsTy = if isPDPatVar patTerms then TCollection mathValueTy else TCollection termExprTy+          (_, bindings, s) <- inferPrimitiveDataPattern patTerms termsTy ctx+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings, s)+        +        PDTermPat patCoeff patMonomials -> do+          -- Term: TermExpr -> Integer, [(SymbolExpr, Integer)]+          -- If patMonomials is pattern variable, it gets [(MathValue, Integer)]+          let symbolExprTy = TSymbolExpr+              mathValueTy = TMathValue+              monomialsElemTy = if isPDPatVar patMonomials+                                then TTuple [mathValueTy, TInt]+                                else TTuple [symbolExprTy, TInt]+          (_, bindings1, s1) <- inferPrimitiveDataPattern patCoeff TInt ctx+          monomialsCollTy <- applySubstWithConstraintsM s1 (TCollection monomialsElemTy)+          (_, bindings2, s2) <- inferPrimitiveDataPattern patMonomials monomialsCollTy ctx+          let s = composeSubst s2 s1+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings1 ++ bindings2, s)+        +        PDSymbolPat patName patIndices -> do+          -- Symbol: SymbolExpr -> String, [IndexExpr]+          -- patName and patIndices types don't change for pattern variables+          let indexExprTy = TIndexExpr+          (_, bindings1, s1) <- inferPrimitiveDataPattern patName TString ctx+          indicesCollTy <- applySubstWithConstraintsM s1 (TCollection indexExprTy)+          (_, bindings2, s2) <- inferPrimitiveDataPattern patIndices indicesCollTy ctx+          let s = composeSubst s2 s1+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings1 ++ bindings2, s)+        +        PDApply1Pat patFn patArg -> do+          -- Apply1: SymbolExpr -> (MathValue -> MathValue), MathValue+          let mathValueTy = TMathValue+              fnTy = TFun mathValueTy mathValueTy+          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx+          mathValueTy' <- applySubstWithConstraintsM s1 mathValueTy+          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg mathValueTy' ctx+          let s = composeSubst s2 s1+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings1 ++ bindings2, s)+        +        PDApply2Pat patFn patArg1 patArg2 -> do+          let mathValueTy = TMathValue+              fnTy = TFun mathValueTy (TFun mathValueTy mathValueTy)+          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx+          mathValueTy1 <- applySubstWithConstraintsM s1 mathValueTy+          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg1 mathValueTy1 ctx+          mathValueTy2 <- applySubstWithConstraintsM s2 mathValueTy+          (_, bindings3, s3) <- inferPrimitiveDataPattern patArg2 mathValueTy2 ctx+          let s = composeSubst s3 (composeSubst s2 s1)+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings1 ++ bindings2 ++ bindings3, s)+        +        PDApply3Pat patFn patArg1 patArg2 patArg3 -> do+          let mathValueTy = TMathValue+              fnTy = TFun mathValueTy (TFun mathValueTy (TFun mathValueTy mathValueTy))+          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx+          mathValueTy1 <- applySubstWithConstraintsM s1 mathValueTy+          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg1 mathValueTy1 ctx+          mathValueTy2 <- applySubstWithConstraintsM s2 mathValueTy+          (_, bindings3, s3) <- inferPrimitiveDataPattern patArg2 mathValueTy2 ctx+          mathValueTy3 <- applySubstWithConstraintsM s3 mathValueTy+          (_, bindings4, s4) <- inferPrimitiveDataPattern patArg3 mathValueTy3 ctx+          let s = composeSubst s4 (composeSubst s3 (composeSubst s2 s1))+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings1 ++ bindings2 ++ bindings3 ++ bindings4, s)+        +        PDApply4Pat patFn patArg1 patArg2 patArg3 patArg4 -> do+          let mathValueTy = TMathValue+              fnTy = TFun mathValueTy (TFun mathValueTy (TFun mathValueTy (TFun mathValueTy mathValueTy)))+          (_, bindings1, s1) <- inferPrimitiveDataPattern patFn fnTy ctx+          mathValueTy1 <- applySubstWithConstraintsM s1 mathValueTy+          (_, bindings2, s2) <- inferPrimitiveDataPattern patArg1 mathValueTy1 ctx+          mathValueTy2 <- applySubstWithConstraintsM s2 mathValueTy+          (_, bindings3, s3) <- inferPrimitiveDataPattern patArg2 mathValueTy2 ctx+          mathValueTy3 <- applySubstWithConstraintsM s3 mathValueTy+          (_, bindings4, s4) <- inferPrimitiveDataPattern patArg3 mathValueTy3 ctx+          mathValueTy4 <- applySubstWithConstraintsM s4 mathValueTy+          (_, bindings5, s5) <- inferPrimitiveDataPattern patArg4 mathValueTy4 ctx+          let s = composeSubst s5 (composeSubst s4 (composeSubst s3 (composeSubst s2 s1)))+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings1 ++ bindings2 ++ bindings3 ++ bindings4 ++ bindings5, s)+        +        PDQuotePat patExpr -> do+          -- Quote: SymbolExpr -> MathValue+          let mathValueTy = TMathValue+          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathValueTy ctx+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings, s)+        +        PDFunctionPat patName patArgs -> do+          -- Function: SymbolExpr -> MathValue, [MathValue]+          let mathValueTy = TMathValue+          (_, bindings1, s1) <- inferPrimitiveDataPattern patName mathValueTy ctx+          argsCollTy <- applySubstWithConstraintsM s1 (TCollection mathValueTy)+          (_, bindings2, s2) <- inferPrimitiveDataPattern patArgs argsCollTy ctx+          expectedType' <- applySubstWithConstraintsM s2 expectedType+          return (expectedType', bindings1 ++ bindings2, s2)+        +        PDSubPat patExpr -> do+          -- Sub: IndexExpr -> MathValue+          let mathValueTy = TMathValue+          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathValueTy ctx+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings, s)++        PDSupPat patExpr -> do+          -- Sup: IndexExpr -> MathValue+          let mathValueTy = TMathValue+          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathValueTy ctx+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings, s)+        +        PDUserPat patExpr -> do+          -- User: IndexExpr -> MathValue+          let mathValueTy = TMathValue+          (_, bindings, s) <- inferPrimitiveDataPattern patExpr mathValueTy ctx+          expectedType' <- applySubstWithConstraintsM s expectedType+          return (expectedType', bindings, s)+  +  -- Match expressions (pattern matching)+  IMatchExpr mode target matcher clauses -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (targetTI, s1) <- inferIExprWithContext target exprCtx+    (matcherTI, s2) <- inferIExprWithContext matcher exprCtx+    let targetType = tiExprType targetTI+        matcherType = tiExprType matcherTI++    -- Matcher should be TMatcher a, (TMatcher a, ...) which becomes TMatcher (a, ...), or a+    -- MatcherSlot (a committed parameter / a stdlib slot-typed matcher).+    let s12 = composeSubst s2 s1+    -- Paper T-MATCHALL / COERCE-MATCHER-TO-SLOT: derive each clause pattern's structural type+    -- τ_p independently and require the matcher to fill MatcherSlot τ_p τ_t.  Run on the raw+    -- matcher type (before the Matcher/tuple normalization below), so an unannotated matcher+    -- parameter — a bare type variable — is committed to a slot type rather than forced into+    -- `Matcher <var>` (indistinguishable from `something`).  Rejects structurally inadmissible+    -- matchers (e.g. `something` at a constructor or concrete-value pattern, even nested).+    sAdm <- checkMatcherAdmissibility exprCtx matcherType targetType clauses s12+    appliedMatcherType <- applySubstWithConstraintsM sAdm matcherType++    -- Normalize the matcher type to extract the matched inner type.+    (_normalizedMatcherType, matchedInnerType, s3) <- case appliedMatcherType of+      TTuple elemTypes -> do+        -- Each tuple element is a Matcher (extract its inner) or a MatcherSlot (its target).+        (finalInnerTypes, s_elems) <- foldM (\(acc, accS) elemTy -> do+          appliedElemTy <- applySubstWithConstraintsM accS elemTy+          case appliedElemTy of+            TMatcherSlot _ tt -> return (acc ++ [tt], accS)+            _ -> do+              innerTy <- freshVar "matched"+              s' <- bindMatcherInner exprCtx appliedElemTy innerTy+              innerTy' <- applySubstWithConstraintsM s' innerTy+              return (acc ++ [innerTy'], composeSubst s' accS)+          ) ([], emptySubst) elemTypes+        -- The tuple as a whole becomes Matcher (a1, a2, ...)+        let tupleInnerType = TTuple finalInnerTypes+        return (TMatcher tupleInnerType, tupleInnerType, s_elems)+      -- A MatcherSlot (committed parameter, or a stdlib slot-typed matcher): the matched inner+      -- type is its target component.+      TMatcherSlot _ tt -> return (TMatcher tt, tt, emptySubst)+      _ -> do+        -- Single matcher: TMatcher a+        matchedTy <- freshVar "matched"+        s' <- bindMatcherInner exprCtx appliedMatcherType matchedTy+        finalMatchedTy <- applySubstWithConstraintsM s' matchedTy+        return (TMatcher finalMatchedTy, finalMatchedTy, s')++    let s123 = composeSubst s3 sAdm+    targetType' <- applySubstWithConstraintsM s123 targetType+    matchedInnerType' <- applySubstWithConstraintsM s123 matchedInnerType+    s4 <- unifyTypesWithContext targetType' matchedInnerType' exprCtx+    +    -- Infer match clauses result type+    let s1234 = composeSubst s4 sAdm+    case clauses of+      [] -> do+        -- No clauses: this should not happen, but handle gracefully+        resultTy <- freshVar "matchResult"+        targetTI' <- applySubstToTIExprM s1234 targetTI+        matcherTI' <- applySubstToTIExprM s1234 matcherTI+        resultTy' <- applySubstWithConstraintsM s1234 resultTy+        return (mkTIExpr resultTy' (TIMatchExpr mode targetTI' matcherTI' []), s1234)+      _ -> do+        -- Infer type of each clause and unify them+        matchedInnerType' <- applySubstWithConstraintsM s1234 matchedInnerType+        (resultTy, clauseTIs, clauseSubst) <- inferMatchClauses exprCtx matchedInnerType' clauses s1234+        let finalS = composeSubst clauseSubst s1234+        targetTI' <- applySubstToTIExprM finalS targetTI+        matcherTI' <- applySubstToTIExprM finalS matcherTI+        resultTy' <- applySubstWithConstraintsM finalS resultTy+        return (mkTIExpr resultTy' (TIMatchExpr mode targetTI' matcherTI' clauseTIs), finalS)+  +  -- MatchAll expressions+  IMatchAllExpr mode target matcher clauses -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (targetTI, s1) <- inferIExprWithContext target exprCtx+    (matcherTI, s2) <- inferIExprWithContext matcher exprCtx+    let targetType = tiExprType targetTI+        matcherType = tiExprType matcherTI+    +    -- Matcher should be TMatcher a, (TMatcher a, ...) which becomes TMatcher (a, ...), or a+    -- MatcherSlot (a committed parameter / a stdlib slot-typed matcher).+    let s12 = composeSubst s2 s1+    -- Paper T-MATCHALL / COERCE-MATCHER-TO-SLOT: derive each clause pattern's structural type+    -- τ_p independently and require the matcher to fill MatcherSlot τ_p τ_t.  Run on the raw+    -- matcher type (before the Matcher/tuple normalization below), so an unannotated matcher+    -- parameter — a bare type variable — is committed to a slot type rather than forced into+    -- `Matcher <var>` (indistinguishable from `something`).  Rejects structurally inadmissible+    -- matchers (e.g. `something` at a constructor or concrete-value pattern, even nested).+    sAdm <- checkMatcherAdmissibility exprCtx matcherType targetType clauses s12+    appliedMatcherType <- applySubstWithConstraintsM sAdm matcherType++    -- Normalize the matcher type to extract the matched inner type.+    (_normalizedMatcherType, matchedInnerType, s3) <- case appliedMatcherType of+      TTuple elemTypes -> do+        -- Each tuple element is a Matcher (extract its inner) or a MatcherSlot (its target).+        (finalInnerTypes, s_elems) <- foldM (\(acc, accS) elemTy -> do+          appliedElemTy <- applySubstWithConstraintsM accS elemTy+          case appliedElemTy of+            TMatcherSlot _ tt -> return (acc ++ [tt], accS)+            _ -> do+              innerTy <- freshVar "matched"+              s' <- bindMatcherInner exprCtx appliedElemTy innerTy+              innerTy' <- applySubstWithConstraintsM s' innerTy+              return (acc ++ [innerTy'], composeSubst s' accS)+          ) ([], emptySubst) elemTypes+        -- The tuple as a whole becomes Matcher (a1, a2, ...)+        let tupleInnerType = TTuple finalInnerTypes+        return (TMatcher tupleInnerType, tupleInnerType, s_elems)+      -- A MatcherSlot (committed parameter, or a stdlib slot-typed matcher): the matched inner+      -- type is its target component.+      TMatcherSlot _ tt -> return (TMatcher tt, tt, emptySubst)+      _ -> do+        -- Single matcher: TMatcher a+        matchedTy <- freshVar "matched"+        s' <- bindMatcherInner exprCtx appliedMatcherType matchedTy+        finalMatchedTy <- applySubstWithConstraintsM s' matchedTy+        return (TMatcher finalMatchedTy, finalMatchedTy, s')++    let s123 = composeSubst s3 sAdm+    targetType' <- applySubstWithConstraintsM s123 targetType+    matchedInnerType' <- applySubstWithConstraintsM s123 matchedInnerType+    s4 <- unifyTypesWithContext targetType' matchedInnerType' exprCtx+    +    -- MatchAll returns a collection of results from match clauses+    let s1234 = composeSubst s4 sAdm+    case clauses of+      [] -> do+        -- No clauses: return empty collection type+        resultElemTy <- freshVar "matchAllElem"+        targetTI' <- applySubstToTIExprM s1234 targetTI+        matcherTI' <- applySubstToTIExprM s1234 matcherTI+        resultElemTy' <- applySubstWithConstraintsM s1234 resultElemTy+        return (mkTIExpr (TCollection resultElemTy') (TIMatchAllExpr mode targetTI' matcherTI' []), s1234)+      _ -> do+        -- Infer type of each clause (they should all have the same type)+        matchedInnerType' <- applySubstWithConstraintsM s1234 matchedInnerType+        (resultElemTy, clauseTIs, clauseSubst) <- inferMatchClauses exprCtx matchedInnerType' clauses s1234+        let finalS = composeSubst clauseSubst s1234+        targetTI' <- applySubstToTIExprM finalS targetTI+        matcherTI' <- applySubstToTIExprM finalS matcherTI+        resultElemTy' <- applySubstWithConstraintsM finalS resultElemTy+        return (mkTIExpr (TCollection resultElemTy') (TIMatchAllExpr mode targetTI' matcherTI' clauseTIs), finalS)+  +  -- Memoized Lambda+  IMemoizedLambdaExpr args body -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    argTypes <- mapM (\_ -> freshVar "memoArg") args+    let bindings = zip args argTypes  -- [(String, Type)]+        schemes = map (\(name, t) -> (name, Forall [] [] t)) bindings+    (bodyTI, s) <- withEnv schemes $ inferIExprWithContext body exprCtx+    let bodyType = tiExprType bodyTI+    finalArgTypes <- mapM (applySubstWithConstraintsM s) argTypes+    let funType = foldr TFun bodyType finalArgTypes+    return (mkTIExpr funType (TIMemoizedLambdaExpr args bodyTI), s)+  +  -- Do expression+  IDoExpr bindings body -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    -- Infer IO monad bindings: each binding should be of type IO a+    env <- getEnv+    (bindingTIs, bindingSchemes, s1) <- inferIOBindingsWithContext bindings env emptySubst exprCtx+    (bodyTI, s2) <- withEnv bindingSchemes $ inferIExprWithContext body exprCtx+    let bodyType = tiExprType bodyTI+        finalS = composeSubst s2 s1+        +    -- Verify that body type is IO a+    bodyResultType <- freshVar "ioResult"+    bodyType' <- applySubstWithConstraintsM finalS bodyType+    s3 <- unifyTypesWithContext bodyType' (TIO bodyResultType) exprCtx+    resultType <- applySubstWithConstraintsM s3 (TIO bodyResultType)+    let finalS' = composeSubst s3 finalS+    return (mkTIExpr resultType (TIDoExpr bindingTIs bodyTI), finalS')+  +  -- Cambda (pattern matching lambda)+  ICambdaExpr var body -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    argType <- freshVar "cambdaArg"+    (bodyTI, s) <- inferIExprWithContext body exprCtx+    let bodyType = tiExprType bodyTI+    return (mkTIExpr (TFun argType bodyType) (TICambdaExpr var bodyTI), s)+  +  -- With symbols+  IWithSymbolsExpr syms body -> do+    -- Add symbols to type environment as MathValue (TMathValue = TInt)+    -- Symbols introduced by withSymbols are mathematical symbols+    let symbolBindings = [(sym, Forall [] [] TMathValue) | sym <- syms]+    (bodyTI, s) <- withEnv symbolBindings $ inferIExprWithContext body ctx+    let bodyType = tiExprType bodyTI+    return (mkTIExpr bodyType (TIWithSymbolsExpr syms bodyTI), s)+  +  -- Quote expressions (symbolic math)+  IQuoteExpr e -> do+    (eTI, s) <- inferIExprWithContext e ctx+    return (mkTIExpr TInt (TIQuoteExpr eTI), s)+  IQuoteSymbolExpr e -> do+    (eTI, s) <- inferIExprWithContext e ctx+    return (mkTIExpr (tiExprType eTI) (TIQuoteSymbolExpr eTI), s)+  +  -- Indexed expression (tensor indexing)+  IIndexedExpr override baseExpr indices -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    -- Special handling for IVarExpr: lookup with Var including index info+    -- Use the same strategy as refVar in Data.hs (Core.hs:235)+    (baseTI, s) <- case baseExpr of+      IVarExpr varName -> do+        -- Convert indices to index types (structure only, no content)+        -- Like: map (fmap (const Nothing)) indices in Core.hs+        let indexTypes = map (fmap (const Nothing)) indices+            varWithIndices = Var varName indexTypes+        env <- getEnv+        -- lookupEnv will try: Var "e" [Sub Nothing, Sub Nothing]+        --                 -> Var "e" [Sub Nothing]+        --                 -> Var "e" []+        case lookupEnv varWithIndices env of+          Just scheme -> do+            st <- get+            let (constraints, t, newCounter) = instantiate scheme (inferCounter st)+            modify $ \s' -> s' { inferCounter = newCounter }+            addConstraints constraints+            return (TIExpr (Forall [] constraints t) (TIVarExpr varName), emptySubst)+          Nothing -> do+            -- No variable found in type environment - fall back to normal inference+            -- This is necessary for lambda parameters, let-bound variables, etc.+            inferIExprWithContext baseExpr exprCtx+      _ -> inferIExprWithContext baseExpr exprCtx+    let baseType = tiExprType baseTI+    -- Infer indices as TIExpr+    indicesTI <- mapM (traverse (\idxExpr -> do+      (idxTI, _) <- inferIExprWithContext idxExpr exprCtx+      return idxTI)) indices+    -- Check if all indices are concrete (constants) or symbolic (variables)+    let isSymbolicIndex idx = case idx of+          Sub (TIExpr _ (TIVarExpr _)) -> True+          Sup (TIExpr _ (TIVarExpr _)) -> True+          SupSub (TIExpr _ (TIVarExpr _)) -> True+          User (TIExpr _ (TIVarExpr _)) -> True+          _ -> False+        hasSymbolicIndex = any isSymbolicIndex indicesTI+    -- For tensors with symbolic indices, keep the tensor type+    -- For concrete indices (numeric), return element type+    let resultType = case baseType of+          TTensor elemType -> +            if hasSymbolicIndex+              then TTensor elemType  -- Symbolic index: keep tensor type+              else elemType           -- Concrete index: element access+          TCollection elemType -> elemType+          THash _keyType valType -> valType  -- Hash access returns value type+          _ -> baseType  -- Fallback: return base type+    return (mkTIExpr resultType (TIIndexedExpr override baseTI indicesTI), s)+  +  -- Subrefs expression (subscript references)+  ISubrefsExpr override baseExpr refExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (baseTI, s1) <- inferIExprWithContext baseExpr exprCtx+    (refTI, s2) <- inferIExprWithContext refExpr exprCtx+    let s12 = composeSubst s2 s1+    -- Constrain the base itself, not only the result.  Otherwise an+    -- unconstrained function parameter remains scalar and the later tensor+    -- elaboration maps the whole function elementwise before evaluation.+    elemType <- freshVar "subrefElem"+    baseType <- applySubstWithConstraintsM s12 (tiExprType baseTI)+    s3 <- unifyTypesWithContext baseType (TTensor elemType) exprCtx+    let finalS = composeSubst s3 s12+    finalElemType <- applySubstWithConstraintsM finalS elemType+    updatedBaseTI <- applySubstToTIExprM finalS baseTI+    updatedRefTI <- applySubstToTIExprM finalS refTI+    let resultType = normalizeTensorType (TTensor finalElemType)+    return (mkTIExpr resultType+              (TISubrefsExpr override updatedBaseTI updatedRefTI), finalS)+  +  -- Suprefs expression (superscript references)+  ISuprefsExpr override baseExpr refExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (baseTI, s1) <- inferIExprWithContext baseExpr exprCtx+    (refTI, s2) <- inferIExprWithContext refExpr exprCtx+    let s12 = composeSubst s2 s1+    elemType <- freshVar "suprefElem"+    baseType <- applySubstWithConstraintsM s12 (tiExprType baseTI)+    s3 <- unifyTypesWithContext baseType (TTensor elemType) exprCtx+    let finalS = composeSubst s3 s12+    finalElemType <- applySubstWithConstraintsM finalS elemType+    updatedBaseTI <- applySubstToTIExprM finalS baseTI+    updatedRefTI <- applySubstToTIExprM finalS refTI+    let resultType = normalizeTensorType (TTensor finalElemType)+    return (mkTIExpr resultType+              (TISuprefsExpr override updatedBaseTI updatedRefTI), finalS)+  +  -- Userrefs expression (user-defined references)+  IUserrefsExpr override baseExpr refExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (baseTI, s1) <- inferIExprWithContext baseExpr exprCtx+    (refTI, s2) <- inferIExprWithContext refExpr exprCtx+    let baseType = tiExprType baseTI+        finalS = composeSubst s2 s1+    -- TODO: Properly handle user-defined references+    return (mkTIExpr baseType (TIUserrefsExpr override baseTI refTI), finalS)++  -- Generate tensor expression+  IGenerateTensorExpr funcExpr shapeExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (funcTI, s1) <- inferIExprWithContext funcExpr exprCtx+    (shapeTI, s2) <- inferIExprWithContext shapeExpr exprCtx+    let funcType = tiExprType funcTI+    -- Extract element type from function result+    elemType <- case funcType of+      TFun _ resultType -> return resultType+      _ -> freshVar "tensorElem"+    let finalS = composeSubst s2 s1+    elemType' <- applySubstWithConstraintsM finalS elemType+    let resultType = normalizeTensorType (TTensor elemType')+    return (mkTIExpr resultType (TIGenerateTensorExpr funcTI shapeTI), finalS)+  +  -- Tensor expression+  ITensorExpr shapeExpr elemsExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (shapeTI, s1) <- inferIExprWithContext shapeExpr exprCtx+    (elemsTI, s2) <- inferIExprWithContext elemsExpr exprCtx+    let elemsType = tiExprType elemsTI+    -- Extract element type+    elemType <- case elemsType of+      TCollection t -> return t+      _ -> freshVar "tensorElem"+    let finalS = composeSubst s2 s1+    elemType' <- applySubstWithConstraintsM finalS elemType+    let resultType = normalizeTensorType (TTensor elemType')+    return (mkTIExpr resultType (TITensorExpr shapeTI elemsTI), finalS)+  +  -- Tensor contract expression+  ITensorContractExpr tensorExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (tensorTI, s1) <- inferIExprWithContext tensorExpr exprCtx+    let tensorType = tiExprType tensorTI+    +    -- contract : Tensor a -> [Tensor a]+    -- Ensure the argument is a Tensor type by unifying with TTensor elemType+    elemType <- freshVar "contractElem"+    tensorType' <- applySubstWithConstraintsM s1 tensorType+    s2 <- unifyTypesWithContext tensorType' (TTensor elemType) exprCtx++    let finalS = composeSubst s2 s1+    finalElemType <- applySubstWithConstraintsM finalS elemType+    let resultType = TCollection (TTensor finalElemType)+    updatedTensorTI <- applySubstToTIExprM finalS tensorTI++    return (mkTIExpr resultType (TITensorContractExpr updatedTensorTI), finalS)+  +  -- Tensor map expression+  ITensorMapExpr func tensorExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (funcTI, s1) <- inferIExprWithContext func exprCtx+    (tensorTI, s2) <- inferIExprWithContext tensorExpr exprCtx+    let funcType = tiExprType funcTI+        tensorType = tiExprType tensorTI+        s12 = composeSubst s2 s1+    -- Function maps elements: a -> b, tensor is Tensor a, result is Tensor b+    case tensorType of+      TTensor elemType -> do+        resultElemType <- freshVar "tmapElem"+        funcType' <- applySubstWithConstraintsM s12 funcType+        s3 <- unifyTypesWithContext funcType' (TFun elemType resultElemType) exprCtx+        let finalS = composeSubst s3 s12+        resultElemType' <- applySubstWithConstraintsM finalS resultElemType+        let resultType = normalizeTensorType (TTensor resultElemType')+        updatedFuncTI <- applySubstToTIExprM finalS funcTI+        updatedTensorTI <- applySubstToTIExprM finalS tensorTI+        return (mkTIExpr resultType (TITensorMapExpr updatedFuncTI updatedTensorTI), finalS)+      _ -> do+        updatedFuncTI <- applySubstToTIExprM s12 funcTI+        updatedTensorTI <- applySubstToTIExprM s12 tensorTI+        return (mkTIExpr tensorType (TITensorMapExpr updatedFuncTI updatedTensorTI), s12)+  +  -- Tensor map2 expression (binary map)+  ITensorMap2Expr func tensor1 tensor2 -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (funcTI, s1) <- inferIExprWithContext func exprCtx+    (tensor1TI, s2) <- inferIExprWithContext tensor1 exprCtx+    (tensor2TI, s3) <- inferIExprWithContext tensor2 exprCtx+    let funcType = tiExprType funcTI+        t1Type = tiExprType tensor1TI+        t2Type = tiExprType tensor2TI+        s123 = foldr composeSubst emptySubst [s3, s2, s1]+    -- Function: a -> b -> c, tensors are Tensor a and Tensor b, result is Tensor c+    case (t1Type, t2Type) of+      (TTensor elem1, TTensor elem2) -> do+        resultElemType <- freshVar "tmap2Elem"+        funcType' <- applySubstWithConstraintsM s123 funcType+        s4 <- unifyTypesWithContext funcType'+                (TFun elem1 (TFun elem2 resultElemType)) exprCtx+        let finalS = composeSubst s4 s123+        resultElemType' <- applySubstWithConstraintsM finalS resultElemType+        let resultType = normalizeTensorType (TTensor resultElemType')+        updatedFuncTI <- applySubstToTIExprM finalS funcTI+        updatedTensor1TI <- applySubstToTIExprM finalS tensor1TI+        updatedTensor2TI <- applySubstToTIExprM finalS tensor2TI+        return (mkTIExpr resultType (TITensorMap2Expr updatedFuncTI updatedTensor1TI updatedTensor2TI), finalS)+      _ -> do+        updatedFuncTI <- applySubstToTIExprM s123 funcTI+        updatedTensor1TI <- applySubstToTIExprM s123 tensor1TI+        updatedTensor2TI <- applySubstToTIExprM s123 tensor2TI+        return (mkTIExpr t1Type (TITensorMap2Expr updatedFuncTI updatedTensor1TI updatedTensor2TI), s123)+  +  -- Transpose expression+  -- ITransposeExpr takes (permutation, tensor) to match tTranspose signature+  ITransposeExpr permExpr tensorExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (permTI, s) <- inferIExprWithContext permExpr exprCtx+    let permType = tiExprType permTI+    -- Unify permutation type with [MathValue]+    permType' <- applySubstWithConstraintsM s permType+    s2 <- unifyTypesWithContext permType' (TCollection TMathValue) exprCtx+    (tensorTI, s3) <- inferIExprWithContext tensorExpr exprCtx+    let finalS = composeSubst s3 (composeSubst s2 s)+    updatedPermTI <- applySubstToTIExprM finalS permTI+    updatedTensorTI <- applySubstToTIExprM finalS tensorTI+    let tensorType = tiExprType updatedTensorTI+    -- Transpose preserves tensor type+    return (mkTIExpr (normalizeTensorType tensorType) (TITransposeExpr updatedPermTI updatedTensorTI), finalS)++  -- Flip indices expression+  IFlipIndicesExpr tensorExpr -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    (tensorTI, s) <- inferIExprWithContext tensorExpr exprCtx+    updatedTensorTI <- applySubstToTIExprM s tensorTI+    let tensorType = tiExprType updatedTensorTI+    -- Flipping indices preserves tensor type+    return (mkTIExpr (normalizeTensorType tensorType) (TIFlipIndicesExpr updatedTensorTI), s)+  +  -- Function symbol expression+  IFunctionExpr names -> do+    -- Function symbols are mathematical function symbols (e.g., f(x,y))+    -- They are represented as MathValue type+    return (mkTIExpr TMathValue (TIFunctionExpr names), emptySubst)++  -- Reshape: type-annotated expression `(e : T)` desugared by Desugar.hs.+  -- Infer e's type, subtype-unify with the annotation, return a TIReshape+  -- node typed as T. At eval time the runtime CAS structure is rewritten+  -- to fit T (or passes through unchanged for non-CAS types).+  IReshape ty inner -> do+    let exprCtx = withExpr (prettyStr expr) ctx+    -- A nested Poly tower may contain at most one open atom set [..]:+    -- with two open slots the atom routing of the runtime reshape would+    -- be ambiguous (see Types.hasAmbiguousOpenTower).+    when (hasAmbiguousOpenTower ty) $+      throwError $ TE.UnsupportedFeature+        ("at most one open atom set [..] may appear in a nested Poly tower: "+         ++ TP.prettyType ty)+        exprCtx+    (innerTI, s) <- inferIExprWithContext inner exprCtx+    let innerType = tiExprType innerTI+    ty' <- applySubstWithConstraintsM s ty+    -- Representation-directive leniency (Phase gamma-prime of the+    -- extensible-tower plan): between CAS-family types the annotation+    -- selects a canonical form of the same value domain (trust the+    -- annotation; the runtime reshape is total on CAS values), so a+    -- structural mismatch such as a nested Poly re-annotated to its flat+    -- form — ((v : Poly (Poly Integer [i]) [x]) : Poly Integer [i, x]) —+    -- must not be a type error. Non-CAS mismatches keep failing.+    s2 <- unifyTypesWithContext innerType ty' exprCtx+            `catchError` \e ->+              if Subtype.isCasType innerType && Subtype.isCasType ty'+                then return emptySubst+                else throwError e+    let finalSubst = composeSubst s2 s+    finalTy <- applySubstWithConstraintsM finalSubst ty+    -- Apply the substitution to innerTI as well so the inner expression's+    -- scheme reflects the unified type. Without this, type-class methods like+    -- `(zero : MathValue)` keep the original `Forall [a] [AddMonoid a] a`+    -- scheme on `zero`, and TypeClassExpand goes down the TVar dispatch path+    -- (emitting a reference to the non-existent `dict_AddMonoid` parameter)+    -- instead of the concrete-instance path. At runtime that produces the+    -- "Expected CASData" / "Expected function" errors typical of unresolved+    -- dispatch.+    innerTI' <- applySubstToTIExprM finalSubst innerTI+    return (mkTIExpr finalTy (TIReshape finalTy innerTI'), finalSubst)++-- | Dual pattern typing for match-site admissibility (paper T-MATCHALL / T-MATCH): a single+-- 'inferIPattern' traversal yields BOTH of the rule's pattern types — τ_t (the target type, from+-- the ordinary inference) and τ_p (the structural type, the 4th component, built from the+-- sub-patterns' own structural types with fresh leaves).  τ_p shares no variable with τ_t (every+-- leaf is fresh and value patterns never contribute their value's type to τ_p), so later unifying+-- τ_t with the target never concretizes τ_p — keeping the structural slot matcher-independent (a+-- bare variable pattern stays admissible for ANY matcher, e.g. `something`).  Run as a+-- side-effect-free probe (type-class constraints and the global zonk substitution+-- snapshotted/restored); any failure falls back to+-- fresh variables (a variable-headed slot admits any matcher — never a false rejection).+patternDualType :: IPattern -> TypeErrorContext -> Infer (Type, Type)+patternDualType pat ctx = do+  snapshot <- saveConstraintState+  result <- (do tv <- freshVar "taut"+                (_, _, st, taup) <- inferIPattern pat tv ctx+                taut  <- applySubstWithConstraintsM st tv+                taup' <- applySubstWithConstraintsM st taup+                return (taup', taut))+              `catchError` \_ -> (,) <$> freshVar "taup" <*> freshVar "taut"+  restoreConstraintState snapshot+  return result++-- | Snapshot of the state that speculative inference must not leak: the+-- type-class constraint store and the global zonk substitution.+saveConstraintState :: Infer ([Constraint], Subst)+saveConstraintState = (,) <$> getConstraints <*> gets inferGlobalSubst++restoreConstraintState :: ([Constraint], Subst) -> Infer ()+restoreConstraintState (cs, g) =+  modify $ \st -> st { inferConstraints = cs, inferGlobalSubst = g }++-- | Run an action but discard its effect on the type-class constraint store and the global+-- zonk substitution.  Used for the structural-type τ_p reassembly unifications, which involve+-- only fresh variables and must never leak into the main (τ_t / binding) inference — including+-- speculative unifications whose failure is swallowed by a 'catchError' fallback.+withIsolatedConstraints :: Infer a -> Infer a+withIsolatedConstraints act = do+  snapshot <- saveConstraintState+  r <- act+  restoreConstraintState snapshot+  return r++-- | τ_p of an and/or/forall/loop/seq-cons pattern: the two sub-patterns describe the same value,+-- so the matcher must support both shapes — unify their structural types (all fresh-headed, so the+-- occurs check cannot fire; isolated so it leaves the main inference untouched).+taupCombine :: TypeErrorContext -> Type -> Type -> Infer Type+taupCombine ctx ta tb = do+  r <- withIsolatedConstraints $+    (do s <- unifyTypesWithContext ta tb ctx+        Just <$> applySubstWithConstraintsM s ta)+      `catchError` \_ -> return Nothing+  case r of+    Just t  -> recordPatfunTaupEqs [(ta, tb)] >> return t+    Nothing -> freshVar "taup"++-- | While inferring a pattern function body, record the structural equations a+-- node-local solver discharged, so PATFUN-DEF can re-solve them jointly for the+-- structural signature (the node-local substitutions themselves are discarded).+-- A no-op outside pattern function bodies.+recordPatfunTaupEqs :: [(Type, Type)] -> Infer ()+recordPatfunTaupEqs eqs =+  modify $ \st -> st { inferPatfunTaupEqs = fmap (eqs ++) (inferPatfunTaupEqs st) }++-- | τ_p of a constructor application: reassemble the constructor's result type from the+-- sub-patterns' structural types @childrenTaup@ at the (freshly instantiated) argument positions+-- @argTypes@ / @resultType@.  Every argument is fresh-headed, so the occurs check can never fire;+-- isolated so the reassembly never leaks into the main inference.+-- | Collect the ~x pattern-variable references of an IPattern in left-to-right+-- order.  Used for the PATFUN-DEF linearity side condition: each pattern+-- function parameter must occur exactly once in the body, in declaration order.+patternVarRefsInOrder :: IPattern -> [String]+patternVarRefsInOrder pat = case pat of+  IVarPat name               -> [name]+  IWildCard                  -> []+  IPatVar _                  -> []+  IValuePat _                -> []+  IPredPat _                 -> []+  IIndexedPat p _            -> patternVarRefsInOrder p+  ILetPat _ p                -> patternVarRefsInOrder p+  INotPat p                  -> patternVarRefsInOrder p+  IAndPat p1 p2              -> patternVarRefsInOrder p1 ++ patternVarRefsInOrder p2+  IOrPat p1 p2               -> patternVarRefsInOrder p1 ++ patternVarRefsInOrder p2+  IForallPat p1 p2           -> patternVarRefsInOrder p1 ++ patternVarRefsInOrder p2+  ITuplePat ps               -> concatMap patternVarRefsInOrder ps+  IInductivePat _ ps         -> concatMap patternVarRefsInOrder ps+  ILoopPat _ (ILoopRange _ _ rp) p1 p2+                             -> patternVarRefsInOrder rp ++ patternVarRefsInOrder p1 ++ patternVarRefsInOrder p2+  IContPat                   -> []+  IPApplyPat _ ps            -> concatMap patternVarRefsInOrder ps+  IInductiveOrPApplyPat _ ps -> concatMap patternVarRefsInOrder ps+  ISeqNilPat                 -> []+  ISeqConsPat p1 p2          -> patternVarRefsInOrder p1 ++ patternVarRefsInOrder p2+  ILaterPatVar               -> []+  IDApplyPat p ps            -> concatMap patternVarRefsInOrder (p : ps)++-- | Collect the ~x pattern-variable references that occur under a branching or+-- repeating pattern (or-, loop-, not-, forall-pattern).  A pattern function+-- parameter in such a position may be expanded zero or several times along a+-- matching path, breaking the PATFUN-DEF binding contract, so PATFUN-DEF+-- rejects it.+patternVarRefsUnderBranch :: IPattern -> [String]+patternVarRefsUnderBranch = go False+  where+    go under pat = case pat of+      IVarPat name               -> [name | under]+      IWildCard                  -> []+      IPatVar _                  -> []+      IValuePat _                -> []+      IPredPat _                 -> []+      IIndexedPat p _            -> go under p+      ILetPat _ p                -> go under p+      INotPat p                  -> go True p+      IAndPat p1 p2              -> go under p1 ++ go under p2+      IOrPat p1 p2               -> go True p1 ++ go True p2+      IForallPat p1 p2           -> go True p1 ++ go True p2+      ITuplePat ps               -> concatMap (go under) ps+      IInductivePat _ ps         -> concatMap (go under) ps+      ILoopPat _ (ILoopRange _ _ rp) p1 p2+                                 -> go True rp ++ go True p1 ++ go True p2+      IContPat                   -> []+      IPApplyPat _ ps            -> concatMap (go under) ps+      IInductiveOrPApplyPat _ ps -> concatMap (go under) ps+      ISeqNilPat                 -> []+      ISeqConsPat p1 p2          -> go under p1 ++ go under p2+      ILaterPatVar               -> []+      IDApplyPat p ps            -> concatMap (go under) (p : ps)++taupFromCtor :: TypeErrorContext -> [Type] -> Type -> [Type] -> Infer Type+taupFromCtor ctx argTypes resultType childrenTaup+  | length argTypes /= length childrenTaup = freshVar "taup"+  | otherwise = do+      r <- withIsolatedConstraints $+        (do s <- foldM (\acc (x, y) -> do+                     x' <- applySubstWithConstraintsM acc x+                     y' <- applySubstWithConstraintsM acc y+                     s' <- unifyTypesWithContext x' y' ctx+                     return (composeSubst s' acc)) emptySubst (zip argTypes childrenTaup)+            Just <$> applySubstWithConstraintsM s resultType)+          `catchError` \_ -> return Nothing+      case r of+        Just t  -> recordPatfunTaupEqs (zip argTypes childrenTaup) >> return t+        Nothing -> freshVar "taup"++-- | Match-site structural admissibility (paper T-MATCHALL / T-MATCH via COERCE-MATCHER-TO-SLOT).+-- For each clause a single traversal ('patternDualType') derives BOTH pattern types at once:+--   * τ_p — the *structural* type, the slot the matcher must fill (one-way @⊑@); a value/predicate+--     pattern contributes only a fresh variable here, so it imposes no structural duty;+--   * τ_t — the *target* type (value patterns contribute their value's type), unified with the+--     target type.+-- The matcher must fill @MatcherSlot τ_p τ_t@.  Routing value-pattern types into τ_t (target)+-- rather than τ_p (structural) means a value pattern `#e` — matched by structural equality @≡@,+-- which every matcher supports — imposes only a target-type constraint.  So `multiset eq with #1`+-- and `something with #1` are admissible, while a *constructor* pattern still demands a+-- structurally-capable matcher (`something` / a bare `Matcher a` at `$x :: $xs` is still rejected).+checkMatcherAdmissibility :: TypeErrorContext -> Type -> Type -> [IMatchClause] -> Subst -> Infer Subst+checkMatcherAdmissibility ctx matcherTy targetTy clauses s0 = foldM step s0 clauses+  where+    step accS (pat, _body) = do+      (tau_p, tau_t) <- patternDualType pat ctx+      targetTy'  <- applySubstWithConstraintsM accS targetTy+      tau_t'     <- applySubstWithConstraintsM accS tau_t+      -- value-pattern-informed type unifies with the actual target type+      sTgt       <- unifyTypesWithContext tau_t' targetTy' ctx+      let acc1   =  composeSubst sTgt accS+      matcherTy' <- applySubstWithConstraintsM acc1 matcherTy+      tau_p'     <- applySubstWithConstraintsM acc1 tau_p+      targetTy'' <- applySubstWithConstraintsM acc1 targetTy+      sSlot <- unifyTypesWithContext matcherTy' (TMatcherSlot tau_p' targetTy'') ctx+      return (composeSubst sSlot acc1)++-- | Head type-former name under which a type's pattern constructors are grouped (paper+-- Coverage, Def 4.2(3)).  Polymorphic / tuple / function matched types have no declared+-- pattern constructors, so they yield 'Nothing' (Coverage holds vacuously).+matcherTypeHead :: Type -> Maybe String+matcherTypeHead t = case t of+  TInt           -> Just "Integer"+  TMathValue     -> Just "MathValue"+  TBool          -> Just "Bool"+  TString        -> Just "String"+  TChar          -> Just "Char"+  TFloat         -> Just "Float"+  TCollection _  -> Just "[]"+  TInductive n _ -> Just n+  _              -> Nothing++-- | The result type-former of a pattern-constructor scheme (walks the @arg -> … -> result@+-- chain), used to group constructors by the type they construct.+ctorResultHead :: TypeScheme -> Maybe String+ctorResultHead (Forall _ _ ty) = matcherTypeHead (resultOf ty)+  where resultOf (TFun _ r) = resultOf r+        resultOf t          = t++-- | The constructor a matcher clause is a *general* clause for (paper's @c $..$@): an+-- inductive primitive-pattern pattern whose arguments are all bare holes @$@.  Refinement+-- clauses (holes mixed with @_@ or @#$x@), value-pattern clauses, and the catch-all are not+-- general clauses and do not contribute to Coverage.+generalClauseCtor :: PrimitivePatPattern -> Maybe String+generalClauseCtor (PPInductivePat name args)+  | all isHole args = Just name+  where isHole PPPatVar = True+        isHole _        = False+generalClauseCtor _ = Nothing++-- | Conservative exhaustiveness of a matcher clause's primitive-data-pattern arms+-- (arm exhaustiveness, paper Def 4.2(1c); enforced as an ordinary type error).+-- An arm set is deemed exhaustive if+-- some arm is irrefutable, or the arms complete a built-in closed shape: the+-- empty-collection pattern together with a cons/snoc pattern with irrefutable components+-- (every collection is empty or uncons-able), or the constants True and False (every Bool).+-- Purely syntactic — no data-constructor enumeration (the type env does not distinguish+-- constructors from functions of the same result type) — so a refutable-but-complete arm+-- set over a user ADT (each constructor enumerated, no final catch-all) is conservatively+-- rejected; add a final `| _ -> []` arm.+pdArmsExhaustive :: [IPrimitiveDataPattern] -> Bool+pdArmsExhaustive arms =+     any pdIrrefutable arms+  || (any isEmptyArm arms && any completeUncons arms)+  || (any (isBoolArm True) arms && any (isBoolArm False) arms)+  where+    isEmptyArm PDEmptyPat = True+    isEmptyArm _          = False+    completeUncons (PDConsPat p1 p2) = pdIrrefutable p1 && pdIrrefutable p2+    completeUncons (PDSnocPat p1 p2) = pdIrrefutable p1 && pdIrrefutable p2+    completeUncons _                 = False+    isBoolArm b (PDConstantPat (BoolExpr b')) = b == b'+    isBoolArm _ _                             = False++-- | An irrefutable primitive data pattern: one that matches every well-typed target.+-- A tuple of irrefutable components is irrefutable because the arm's expected type is+-- unified with the corresponding tuple type, so the target is always a tuple of that+-- arity in a well-typed program.+pdIrrefutable :: IPrimitiveDataPattern -> Bool+pdIrrefutable PDWildCard      = True+pdIrrefutable (PDPatVar _)    = True+pdIrrefutable (PDTuplePat ps) = all pdIrrefutable ps+pdIrrefutable _               = False++-- | Infer match clauses type+-- All clauses should return the same type+-- NEW: Returns TIMatchClause list in addition to type and subst+inferMatchClauses :: TypeErrorContext -> Type -> [IMatchClause] -> Subst -> Infer (Type, [TIMatchClause], Subst)+inferMatchClauses ctx matchedType clauses initSubst = do+  case clauses of+    [] -> do+      -- No clauses (should not happen)+      ty <- freshVar "clauseResult"+      return (ty, [], initSubst)+    (firstClause:restClauses) -> do+      -- Infer first clause+      (firstTI, firstType, s1) <- inferMatchClause ctx matchedType firstClause initSubst+      +      -- Infer rest clauses and unify with first+      (finalType, clauseTIs, finalSubst) <- foldM (inferAndUnifyClause ctx matchedType) (firstType, [firstTI], s1) restClauses+      return (finalType, reverse clauseTIs, finalSubst)+  where+    inferAndUnifyClause :: TypeErrorContext -> Type -> (Type, [TIMatchClause], Subst) -> IMatchClause -> Infer (Type, [TIMatchClause], Subst)+    inferAndUnifyClause ctx' matchedTy (expectedType, accClauses, accSubst) clause = do+      matchedTy' <- applySubstWithConstraintsM accSubst matchedTy+      (clauseTI, clauseType, s1) <- inferMatchClause ctx' matchedTy' clause accSubst+      expectedType' <- applySubstWithConstraintsM s1 expectedType+      s2 <- unifyTypesWithContext expectedType' clauseType ctx'+      let finalS = composeSubst s2 (composeSubst s1 accSubst)+      finalExpectedType <- applySubstWithConstraintsM finalS expectedType+      return (finalExpectedType, clauseTI : accClauses, finalS)++-- | Infer a single match clause+-- NEW: Returns TIMatchClause in addition to type and subst+inferMatchClause :: TypeErrorContext -> Type -> IMatchClause -> Subst -> Infer (TIMatchClause, Type, Subst)+inferMatchClause ctx matchedType (pattern, bodyExpr) initSubst = do+  -- Infer pattern type and extract pattern variable bindings+  -- Use pattern constructor and pattern function type information+  (tiPattern, bindings, s_pat, _) <- inferIPattern pattern matchedType ctx+  let s1 = composeSubst s_pat initSubst+  +  -- Convert bindings to TypeScheme format+  let schemes = [(var, Forall [] [] ty) | (var, ty) <- bindings]+  +  -- Infer body expression type with pattern variables in scope+  (bodyTI, s2) <- withEnv schemes $ inferIExprWithContext bodyExpr ctx+  let bodyType = tiExprType bodyTI+      finalS = composeSubst s2 s1+  finalBodyType <- applySubstWithConstraintsM finalS bodyType+  return ((tiPattern, bodyTI), finalBodyType, finalS)++-- | Infer multiple patterns left-to-right, making left bindings available to right patterns+-- This enables non-linear patterns like ($p, #(p + 1))+-- Returns (list of TIPattern, accumulated bindings, substitution)+-- The final @[Type]@ component is the sub-patterns' structural types τ_p, in order, used by the+-- parent constructor/tuple to reassemble its own τ_p (paper T-MATCHALL dual judgment).+inferPatternsLeftToRight :: [IPattern] -> [Type] -> [(String, Type)] -> Subst -> TypeErrorContext+                         -> Infer ([TIPattern], [(String, Type)], Subst, [Type])+inferPatternsLeftToRight [] [] accBindings accSubst _ctx =+  return ([], accBindings, accSubst, [])+inferPatternsLeftToRight (p:ps) (t:ts) accBindings accSubst ctx = do+  -- Add accumulated bindings to environment for this pattern+  let schemes = [(var, Forall [] [] ty) | (var, ty) <- accBindings]++  -- Infer this pattern with left bindings in scope+  t' <- applySubstWithConstraintsM accSubst t+  (tipat, newBindings, s, taup) <- withEnv schemes $ inferIPattern p t' ctx++  -- Compose substitutions+  let accSubst' = composeSubst s accSubst++  -- Apply substitution to accumulated bindings+  accBindings'' <- mapM (\(v, ty) -> do+      ty' <- applySubstWithConstraintsM s ty+      return (v, ty')) accBindings+  let accBindings' = accBindings'' ++ newBindings++  -- Continue with remaining patterns+  (restTipats, finalBindings, finalSubst, restTaups) <- inferPatternsLeftToRight ps ts accBindings' accSubst' ctx+  return (tipat : restTipats, finalBindings, finalSubst, taup : restTaups)+inferPatternsLeftToRight _ _ accBindings accSubst _ =+  return ([], accBindings, accSubst, [])  -- Mismatched lengths++-- | Infer an IPattern's types and extract its pattern-variable bindings.  Returns+-- (TIPattern, bindings, substitution, τ_p) — realizing the paper's dual judgment+-- @Γ;Δ ⊢ p : Pattern τ_p ▷ τ_t ; Δ'@ in one traversal:+--   * τ_t (the *target* type) and Δ' (bindings) are computed exactly as before — coherently,+--     top-down, threading one substitution (τ_t is read from the TIPattern / @expectedType@);+--   * τ_p (the *structural* type, the 4th component) is built up from the sub-patterns' own τ_p+--     with a FRESH variable at every leaf (variable, wildcard, and value/predicate position).+-- Keeping τ_p's variables disjoint from τ_t's (the leaves are fresh, and value patterns never+-- contribute their value's type to τ_p) is what lets the later τ_t-with-target unification leave+-- τ_p untouched — so the structural slot stays matcher-independent — and is also what stops τ_p+-- from tangling with outer bindings into an infinite type.  The few τ_p reassembly unifications+-- (constructor/and/or/…) are run with 'withIsolatedConstraints' so they never leak into the main+-- (τ_t / binding) inference.+inferIPattern :: IPattern -> Type -> TypeErrorContext -> Infer (TIPattern, [(String, Type)], Subst, Type)+inferIPattern pat expectedType ctx = case pat of+  IWildCard -> do+    -- Wildcard: no bindings; τ_p is a fresh variable (no structural duty)+    let tipat = TIPattern (Forall [] [] expectedType) TIWildCard+    taup <- freshVar "taup"+    return (tipat, [], emptySubst, taup)++  IPatVar name -> do+    -- Pattern variable: bind to expected type; τ_p a fresh variable, independent of expectedType+    let tipat = TIPattern (Forall [] [] expectedType) (TIPatVar name)+    taup <- freshVar "taup"+    return (tipat, [(name, expectedType)], emptySubst, taup)++  IValuePat expr -> do+    -- Value pattern: infer expression type and unify with expected type.  τ_p is a fresh variable+    -- — matched by structural equality, the value imposes no structural duty (its type goes to τ_t)+    (exprTI, s) <- inferIExprWithContext expr ctx+    let exprType = tiExprType exprTI+    exprType' <- applySubstWithConstraintsM s exprType+    expectedType' <- applySubstWithConstraintsM s expectedType+    s' <- unifyTypesWithContext exprType' expectedType' ctx+    let finalS = composeSubst s' s+    exprTI' <- applySubstToTIExprM finalS exprTI+    finalType <- applySubstWithConstraintsM finalS expectedType+    let tipat = TIPattern (Forall [] [] finalType) (TIValuePat exprTI')+    taup <- freshVar "taup"+    return (tipat, [], finalS, taup)++  IPredPat expr -> do+    -- Predicate pattern: infer predicate expression.  τ_p is a fresh variable (a boolean test+    -- imposes no structural duty)+    let predicateType = TFun expectedType TBool+    (exprTI, s) <- inferIExprWithContext expr ctx+    -- Unify with expected predicate type to concretize type variables+    exprType' <- applySubstWithConstraintsM s (tiExprType exprTI)+    predicateType' <- applySubstWithConstraintsM s predicateType+    s' <- unifyTypesWithContext exprType' predicateType' ctx+    let finalS = composeSubst s' s+    exprTI' <- applySubstToTIExprM finalS exprTI+    finalType <- applySubstWithConstraintsM finalS expectedType+    let tipat = TIPattern (Forall [] [] finalType) (TIPredPat exprTI')+    taup <- freshVar "taup"+    return (tipat, [], finalS, taup)+  +  ITuplePat pats -> do+    -- Tuple pattern: decompose expected type+    case expectedType of+      TTuple types | length types == length pats -> do+        -- Types match: infer each sub-pattern left-to-right+        -- Left patterns' bindings are available for right patterns (for non-linear patterns)+        (tipats, allBindings, s, childrenTaup) <- inferPatternsLeftToRight pats types [] emptySubst ctx+        finalType <- applySubstWithConstraintsM s expectedType+        let tipat = TIPattern (Forall [] [] finalType) (TITuplePat tipats)+        return (tipat, allBindings, s, TTuple childrenTaup)++      TVar _ -> do+        -- Expected type is a type variable: create tuple type+        elemTypes <- mapM (\_ -> freshVar "elem") pats+        let tupleTy = TTuple elemTypes+        s <- unifyTypesWithContext expectedType tupleTy ctx++        -- Recursively infer each sub-pattern left-to-right+        elemTypes' <- mapM (applySubstWithConstraintsM s) elemTypes+        (tipats, allBindings, s', childrenTaup) <- inferPatternsLeftToRight pats elemTypes' [] s ctx+        finalType <- applySubstWithConstraintsM s' expectedType+        let tipat = TIPattern (Forall [] [] finalType) (TITuplePat tipats)+        return (tipat, allBindings, s', TTuple childrenTaup)+      +      _ -> do+        -- Type mismatch+        throwError $ TE.TypeMismatch+          (TTuple (replicate (length pats) (TVar (TyVar "a"))))+          expectedType+          "Tuple pattern but matched type is not a tuple"+          ctx+  +  IInductivePat name pats -> do+    -- Inductive pattern: look up pattern constructor type from pattern environment+    patternEnv <- getPatternEnv+    case lookupPatternEnv name patternEnv of+      Just scheme -> do+        -- Found in pattern environment: use the declared type+        st <- get+        let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)+        modify $ \s -> s { inferCounter = newCounter }+        +        -- Pattern constructor type: arg1 -> arg2 -> ... -> resultType+        let (argTypes, resultType) = extractFunctionArgs ctorType+        +        -- Check argument count matches+        if length argTypes /= length pats+          then throwError $ TE.TypeMismatch+                 (foldr TFun resultType (replicate (length pats) (TVar (TyVar "a"))))+                 ctorType+                 ("Pattern constructor " ++ name ++ " expects " ++ show (length argTypes) +                  ++ " arguments, but got " ++ show (length pats))+                 ctx+          else do+            -- Unify result type with expected type+            s0 <- unifyTypesWithContext resultType expectedType ctx+            argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes++            -- Recursively infer each sub-pattern left-to-right+            -- Left patterns' bindings are available for right patterns+            (tipats, allBindings, s, childrenTaup) <- inferPatternsLeftToRight pats argTypes' [] s0 ctx+            finalType <- applySubstWithConstraintsM s expectedType+            -- τ_p: reassemble from a FRESH instantiation (untied to expectedType) + children τ_p+            stP <- get+            let (_csP, ctorTypeP, ctrP) = instantiate scheme (inferCounter stP)+            modify $ \z -> z { inferCounter = ctrP }+            let (argTypesP, resultTypeP) = extractFunctionArgs ctorTypeP+            taup <- taupFromCtor ctx argTypesP resultTypeP childrenTaup+            let tipat = TIPattern (Forall [] [] finalType) (TIInductivePat name tipats)+            return (tipat, allBindings, s, taup)+      +      Nothing -> do+        -- Not found in pattern environment: try data constructor from value environment+        -- This handles data constructors used as patterns+        env <- getEnv+        case lookupEnv (stringToVar name) env of+          Just scheme -> do+            st <- get+            let (_constraints, ctorType, newCounter) = instantiate scheme (inferCounter st)+            modify $ \s -> s { inferCounter = newCounter }+            +            let (argTypes, resultType) = extractFunctionArgs ctorType+            +            if length argTypes /= length pats+              then throwError $ TE.TypeMismatch+                     (foldr TFun resultType (replicate (length pats) (TVar (TyVar "a"))))+                     ctorType+                     ("Constructor " ++ name ++ " expects " ++ show (length argTypes) +                      ++ " arguments, but got " ++ show (length pats))+                     ctx+              else do+                s0 <- unifyTypesWithContext resultType expectedType ctx+                argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes++                -- Recursively infer each sub-pattern left-to-right+                (tipats, allBindings, s, childrenTaup) <- inferPatternsLeftToRight pats argTypes' [] s0 ctx+                finalType <- applySubstWithConstraintsM s expectedType+                -- τ_p: reassemble from a FRESH instantiation (untied to expectedType) + children τ_p+                stP <- get+                let (_csP, ctorTypeP, ctrP) = instantiate scheme (inferCounter stP)+                modify $ \z -> z { inferCounter = ctrP }+                let (argTypesP, resultTypeP) = extractFunctionArgs ctorTypeP+                taup <- taupFromCtor ctx argTypesP resultTypeP childrenTaup+                let tipat = TIPattern (Forall [] [] finalType) (TIInductivePat name tipats)+                return (tipat, allBindings, s, taup)++          Nothing -> do+            -- Not found: generic inference+            argTypes <- mapM (\_ -> freshVar "arg") pats+            let resultType = TInductive name argTypes++            s0 <- unifyTypesWithContext resultType expectedType ctx+            argTypes' <- mapM (applySubstWithConstraintsM s0) argTypes++            -- Recursively infer each sub-pattern left-to-right+            (tipats, allBindings, s, childrenTaup) <- inferPatternsLeftToRight pats argTypes' [] s0 ctx+            finalType <- applySubstWithConstraintsM s expectedType+            -- τ_p: an undeclared constructor is treated generically — same head, children's τ_p+            let tipat = TIPattern (Forall [] [] finalType) (TIInductivePat name tipats)+            return (tipat, allBindings, s, TInductive name childrenTaup)+  +  IIndexedPat p indices -> do+    -- Indexed pattern: infer base pattern and index expressions+    -- For $x_i pattern, x should have type Hash keyType expectedType+    -- where expectedType is the type of the indexed result+    +    -- First, infer the index expressions to determine their types+    indexTypes <- mapM (\_ -> freshVar "idx") indices+    (indexTIs, s1) <- foldM (\(accTIs, accS) (idx, idxType) -> do+      (idxTI, idxS) <- inferIExprWithContext idx ctx+      let actualIdxType = tiExprType idxTI+      actualIdxType' <- applySubstWithConstraintsM idxS actualIdxType+      idxType' <- applySubstWithConstraintsM idxS idxType+      s' <- unifyTypesWithContext actualIdxType' idxType' ctx+      let finalS = composeSubst s' (composeSubst idxS accS)+      return (accTIs ++ [idxTI], finalS)) ([], emptySubst) (zip indices indexTypes)++    -- Construct the base type: Hash indexType expectedType+    -- For simplicity, assume single index access and use THash+    indexType <- case indexTypes of+                   [t] -> applySubstWithConstraintsM s1 t+                   _ -> return TInt  -- Multiple indices: fallback to Int+    let baseType = THash indexType expectedType++    -- Infer base pattern with Hash type+    baseType' <- applySubstWithConstraintsM s1 baseType+    (tipat, bindings, s2, _) <- inferIPattern p baseType' ctx++    let finalS = composeSubst s2 s1+    finalType <- applySubstWithConstraintsM finalS expectedType+    let tiIndexedPat = TIPattern (Forall [] [] finalType) (TIIndexedPat tipat indexTIs)+    -- τ_p: an indexed access ($x_i) is variable-like — no structural duty+    taup <- freshVar "taup"+    return (tiIndexedPat, bindings, finalS, taup)+  +  ILetPat bindings p -> do+    -- Let pattern: infer bindings and then the pattern+    -- Infer bindings first+    env <- getEnv+    (bindingTIs, bindingSchemes, s1) <- inferIBindingsWithContext bindings env emptySubst ctx++    -- Infer pattern with bindings in scope+    expectedType' <- applySubstWithConstraintsM s1 expectedType+    (tipat, patBindings, s2, innerTaup) <- withEnv bindingSchemes $ inferIPattern p expectedType' ctx++    let s = composeSubst s2 s1+    finalType <- applySubstWithConstraintsM s expectedType+    let tiLetPat = TIPattern (Forall [] [] finalType) (TILetPat bindingTIs tipat)+    -- Let bindings are not exported, only pattern bindings; τ_p is the inner pattern's+    return (tiLetPat, patBindings, s, innerTaup)++  INotPat p -> do+    -- Not pattern: infer the sub-pattern but don't use its bindings; τ_p is the inner pattern's+    (tipat, _, s, innerTaup) <- inferIPattern p expectedType ctx+    finalType <- applySubstWithConstraintsM s expectedType+    let tiNotPat = TIPattern (Forall [] [] finalType) (TINotPat tipat)+    return (tiNotPat, [], s, innerTaup)+  +  IAndPat p1 p2 -> do+    -- And pattern: both patterns must match the same type+    -- Left bindings should be available to right pattern+    (tipat1, bindings1, s1, taup1) <- inferIPattern p1 expectedType ctx+    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]+    expectedType' <- applySubstWithConstraintsM s1 expectedType+    (tipat2, bindings2, s2, taup2) <- withEnv schemes1 $ inferIPattern p2 expectedType' ctx+    let s = composeSubst s2 s1+    -- Apply substitution to left bindings+    bindings1'' <- mapM (\(v, ty) -> do+        ty' <- applySubstWithConstraintsM s2 ty+        return (v, ty')) bindings1+    finalType <- applySubstWithConstraintsM s expectedType+    -- τ_p: the matcher must support both conjuncts' shapes+    taup <- taupCombine ctx taup1 taup2+    let bindings1' = bindings1''+        tiAndPat = TIPattern (Forall [] [] finalType) (TIAndPat tipat1 tipat2)+    return (tiAndPat, bindings1' ++ bindings2, s, taup)+  +  IOrPat p1 p2 -> do+    -- Or pattern (paper PAT-OR): the two branches are alternatives over the same input+    -- context, so they are typed independently and must produce the SAME output bindings Δ'+    -- — the same variable names, at unifiable types.+    (tipat1, bindings1, s1, taup1) <- inferIPattern p1 expectedType ctx+    expectedType' <- applySubstWithConstraintsM s1 expectedType+    (tipat2, bindings2, s2, taup2) <- inferIPattern p2 expectedType' ctx+    let s12 = composeSubst s2 s1+        vars1 = nub (map fst bindings1)+        vars2 = nub (map fst bindings2)+        sameVars = all (`elem` vars2) vars1 && all (`elem` vars1) vars2+    if not sameVars+      then throwError $ TE.TypeMismatch+             (TTuple (map snd bindings1))+             (TTuple (map snd bindings2))+             ("or-pattern (`|`) branches must bind the same variables, but the left binds {"+               ++ intercalate ", " vars1 ++ "} and the right binds {"+               ++ intercalate ", " vars2 ++ "}")+             ctx+      else do+        -- Unify the type of each shared variable across the two branches.+        sVars <- foldM (\accS (v, ty1) ->+            case lookup v bindings2 of+              Just ty2 -> do+                ty1' <- applySubstWithConstraintsM accS ty1+                ty2' <- applySubstWithConstraintsM accS ty2+                s' <- unifyTypesWithContext ty1' ty2' ctx+                return (composeSubst s' accS)+              Nothing -> return accS+          ) s12 bindings1+        finalBindings <- mapM (\(v, ty) -> do+            ty' <- applySubstWithConstraintsM sVars ty+            return (v, ty')) bindings1+        finalType <- applySubstWithConstraintsM sVars expectedType+        let tiOrPat = TIPattern (Forall [] [] finalType) (TIOrPat tipat1 tipat2)+        -- τ_p: the matcher must support either alternative's shape+        taup <- taupCombine ctx taup1 taup2+        return (tiOrPat, finalBindings, sVars, taup)+  +  IForallPat p1 p2 -> do+    -- Forall pattern: similar to and pattern+    -- Left bindings should be available to right pattern+    (tipat1, bindings1, s1, taup1) <- inferIPattern p1 expectedType ctx+    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]+    expectedType' <- applySubstWithConstraintsM s1 expectedType+    (tipat2, bindings2, s2, taup2) <- withEnv schemes1 $ inferIPattern p2 expectedType' ctx+    let s = composeSubst s2 s1+    -- Apply substitution to left bindings+    bindings1'' <- mapM (\(v, ty) -> do+        ty' <- applySubstWithConstraintsM s2 ty+        return (v, ty')) bindings1+    finalType <- applySubstWithConstraintsM s expectedType+    taup <- taupCombine ctx taup1 taup2+    let bindings1' = bindings1''+        tiForallPat = TIPattern (Forall [] [] finalType) (TIForallPat tipat1 tipat2)+    return (tiForallPat, bindings1' ++ bindings2, s, taup)+  +  ILoopPat var range p1 p2 -> do+    -- Loop pattern: $var is the loop variable (Integer), range contains pattern+    -- First, infer the range pattern (third element of ILoopRange)+    let ILoopRange startExpr endExpr rangePattern = range+    (tiRangePat, rangeBindings, s_range, _) <- inferIPattern rangePattern TInt ctx+    +    -- Infer start and end expressions+    (startTI, s_start) <- inferIExprWithContext startExpr ctx+    (endTI, s_end) <- inferIExprWithContext endExpr ctx+    let tiLoopRange = TILoopRange startTI endTI tiRangePat+    +    -- Add loop variable binding (always Integer for loop index)+    let loopVarBinding = (var, TInt)+        initialBindings = loopVarBinding : rangeBindings+        schemes0 = [(v, Forall [] [] ty) | (v, ty) <- initialBindings]+        s_combined = foldr composeSubst emptySubst [s_end, s_start, s_range]++    -- Infer p1 with loop variable and range bindings in scope+    expectedType1 <- applySubstWithConstraintsM s_combined expectedType+    (tipat1, bindings1, s1, taup1) <- withEnv schemes0 $ inferIPattern p1 expectedType1 ctx++    -- Infer p2 with all previous bindings in scope+    allPrevBindings' <- mapM (\(v, ty) -> do+        ty' <- applySubstWithConstraintsM s1 ty+        return (v, ty')) initialBindings+    let allPrevBindings = allPrevBindings' ++ bindings1+        schemes1 = [(v, Forall [] [] ty) | (v, ty) <- allPrevBindings]+    expectedType2 <- applySubstWithConstraintsM s1 expectedType+    (tipat2, bindings2, s2, taup2) <- withEnv schemes1 $ inferIPattern p2 expectedType2 ctx++    let s = foldr composeSubst emptySubst [s2, s1, s_combined]+    -- Apply final substitution to all bindings+    finalBindings' <- mapM (\(v, ty) -> do+        ty' <- applySubstWithConstraintsM s ty+        return (v, ty')) (loopVarBinding : rangeBindings ++ bindings1 ++ bindings2)+    finalType <- applySubstWithConstraintsM s expectedType+    -- τ_p: the iterated body and the rest both describe the matched value+    taup <- taupCombine ctx taup1 taup2+    let finalBindings = finalBindings'+        tiLoopPat = TIPattern (Forall [] [] finalType) (TILoopPat var tiLoopRange tipat1 tipat2)++    return (tiLoopPat, finalBindings, s, taup)++  IContPat -> do+    -- Continuation pattern: no bindings+    let tipat = TIPattern (Forall [] [] expectedType) TIContPat+    taup <- freshVar "taup"+    return (tipat, [], emptySubst, taup)+  +  IPApplyPat funcExpr argPats -> do+    -- Pattern application (paper PAT-APP), the same device as IInductivePat:+    -- the target side unifies the pattern function's type with the argument+    -- target types and the expected (target) type; the structural side+    -- instantiates the function's recorded structural signature and unifies+    -- the arguments' structural indices into it.+    (funcTI, s1) <- inferIExprWithContext funcExpr ctx++    -- Target side: f : tau_1 -> ... -> tau_k -> tau (inner types, no Pattern+    -- wrapper; design/pattern.md), so unify it with parg_1 -> ... -> parg_k ->+    -- expectedType and check the argument patterns against the resolved+    -- parameter types (top-down, keeping tau_t coherent).+    argTypes <- mapM (\_ -> freshVar "parg") argPats+    let funcType = tiExprType funcTI+    funcType' <- applySubstWithConstraintsM s1 funcType+    expectedType1 <- applySubstWithConstraintsM s1 expectedType+    s0 <- unifyTypesWithContext funcType' (foldr TFun expectedType1 argTypes) ctx+    let s10 = composeSubst s0 s1+    argTypes' <- mapM (applySubstWithConstraintsM s10) argTypes+    (tipats, allBindings, s2, childrenTaup) <- inferPatternsLeftToRight argPats argTypes' [] s10 ctx++    finalType <- applySubstWithConstraintsM s2 expectedType+    let tipat = TIPattern (Forall [] [] finalType) (TIPApplyPat funcTI tipats)++    -- Structural side: instantiate the structural signature+    -- beta_1 -> ... -> beta_k -> tau_p_body recorded at the definition+    -- (a FRESH instantiation, untied to the target side, as in IInductivePat),+    -- unify the arguments' structural indices with the beta_i, and return the+    -- resulting instance of tau_p_body.  Without a recorded signature the+    -- application is structurally unconstrained (fresh), the pre-fix behavior.+    taup <- case funcExpr of+      IVarExpr fname -> do+        structEnv <- getPatternFuncStructEnvI+        case lookupPatternEnv fname structEnv of+          Just structScheme -> do+            stP <- get+            let (_csP, structTy, ctrP) = instantiate structScheme (inferCounter stP)+            modify $ \z -> z { inferCounter = ctrP }+            let (argTaups, resultTaup) = extractFunctionArgs structTy+            taupFromCtor ctx argTaups resultTaup childrenTaup+          Nothing -> freshVar "taup"+      _ -> freshVar "taup"+    return (tipat, allBindings, s2, taup)++  IVarPat name -> do+    -- Variable pattern (with ~): bind to expected type.+    -- τ_p: inside a pattern function body, a parameter embedding ~x_i carries the+    -- parameter's structural index beta_i (paper PATFUN-DEF/PAT-EMBED), so the body's+    -- structural index records where each argument's structure flows; otherwise fresh.+    let tipat = TIPattern (Forall [] [] expectedType) (TIVarPat name)+    paramTaups <- inferPatfunParamTaup <$> get+    taup <- case Map.lookup name paramTaups of+              Just beta -> return beta+              Nothing   -> freshVar "taup"+    return (tipat, [(name, expectedType)], emptySubst, taup)+  +  IInductiveOrPApplyPat name pats -> do+    -- Could be either inductive pattern or pattern application+    -- Check pattern function environment to distinguish+    -- Pattern functions are ONLY in patternFuncEnv, pattern constructors are NOT+    patternFuncEnv <- getPatternFuncEnv+    case lookupPatternEnv name patternFuncEnv of+      Just _ -> do+        -- It's a pattern function: treat as pattern application+        (tipat, bindings, s, taup) <- inferIPattern (IPApplyPat (IVarExpr name) pats) expectedType ctx+        return (tipat, bindings, s, taup)+      Nothing -> do+        -- It's an inductive pattern constructor (or not found, will be handled later)+        (tipat, bindings, s, taup) <- inferIPattern (IInductivePat name pats) expectedType ctx+        -- Wrap it as InductiveOrPApplyPat (if it's actually an inductive pattern)+        case tipPatternNode tipat of+          TIInductivePat _ tipats -> do+            let scheme = tipScheme tipat+                tiInductiveOrPApplyPat = TIPattern scheme (TIInductiveOrPApplyPat name tipats)+            return (tiInductiveOrPApplyPat, bindings, s, taup)+          _ ->+            -- Not an inductive pattern (e.g., already processed as pattern application)+            return (tipat, bindings, s, taup)+  +  ISeqNilPat -> do+    -- Sequence nil: no bindings+    let tipat = TIPattern (Forall [] [] expectedType) TISeqNilPat+    taup <- freshVar "taup"+    return (tipat, [], emptySubst, taup)++  ISeqConsPat p1 p2 -> do+    -- Sequence cons: infer both patterns+    -- Left bindings should be available to right pattern+    (tipat1, bindings1, s1, taup1) <- inferIPattern p1 expectedType ctx+    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]+    expectedType' <- applySubstWithConstraintsM s1 expectedType+    (tipat2, bindings2, s2, taup2) <- withEnv schemes1 $ inferIPattern p2 expectedType' ctx+    let s = composeSubst s2 s1+    -- Apply substitution to left bindings+    bindings1'' <- mapM (\(v, ty) -> do+        ty' <- applySubstWithConstraintsM s2 ty+        return (v, ty')) bindings1+    finalType <- applySubstWithConstraintsM s expectedType+    taup <- taupCombine ctx taup1 taup2+    let bindings1' = bindings1''+        tipat = TIPattern (Forall [] [] finalType) (TISeqConsPat tipat1 tipat2)+    return (tipat, bindings1' ++ bindings2, s, taup)++  ILaterPatVar -> do+    -- Later pattern variable: no immediate binding+    let tipat = TIPattern (Forall [] [] expectedType) TILaterPatVar+    taup <- freshVar "taup"+    return (tipat, [], emptySubst, taup)+  +  IDApplyPat p pats -> do+    -- D-apply pattern: infer base pattern and argument patterns+    -- Base pattern bindings should be available to argument patterns+    (tipat, bindings1, s1, baseTaup) <- inferIPattern p expectedType ctx++    -- Infer argument patterns left-to-right with base pattern bindings in scope+    argTypes <- mapM (\_ -> freshVar "darg") pats+    let schemes1 = [(var, Forall [] [] ty) | (var, ty) <- bindings1]+    (tipats, argBindings, s2, _) <- withEnv schemes1 $ inferPatternsLeftToRight pats argTypes [] s1 ctx++    let s = composeSubst s2 s1+    -- Apply substitution to base bindings+    bindings1'' <- mapM (\(v, ty) -> do+        ty' <- applySubstWithConstraintsM s2 ty+        return (v, ty')) bindings1+    finalType <- applySubstWithConstraintsM s expectedType+    let bindings1' = bindings1''+        tiDApplyPat = TIPattern (Forall [] [] finalType) (TIDApplyPat tipat tipats)+    -- τ_p: the d-apply's structural shape is its base pattern's+    return (tiDApplyPat, bindings1' ++ argBindings, s, baseTaup)+  where+    -- Extract function argument types and result type+    -- e.g., a -> b -> c -> d  =>  ([a, b, c], d)+    extractFunctionArgs :: Type -> ([Type], Type)+    extractFunctionArgs (TFun arg rest) = +      let (args, result) = extractFunctionArgs rest+      in (arg : args, result)+    extractFunctionArgs t = ([], t)++-- | Infer application (helper)+-- NEW: Returns TIExpr instead of (IExpr, Type, Subst)+inferIApplication :: String -> Type -> [IExpr] -> Subst -> Infer (TIExpr, Subst)+inferIApplication funcName funcType args initSubst = do+  let funcTI = mkTIExpr funcType (TIVarExpr funcName)+  inferIApplicationWithContext funcTI funcType args initSubst emptyContext++-- TensorMap insertion logic has been moved to Language.Egison.Type.TensorMapInsertion+-- This keeps type inference focused on type checking only++-- | Infer application (helper) with context+-- NEW: Returns TIExpr instead of (IExpr, Type, Subst)+-- TensorMap insertion has been moved to Phase 7 (TensorMapInsertion module)+-- This function now only performs type inference and unification+-- When a Tensor argument is passed to a scalar parameter, the result type is wrapped in Tensor+--+-- IMPORTANT: Non-function arguments are unified first to let data types (like lists)+-- constrain type variables before callback function types are unified.+-- This ensures that foldl (+) 0 [t1, t2] properly infers a = Tensor Integer from the list+-- before trying to match the callback type.+inferIApplicationWithContext :: TIExpr -> Type -> [IExpr] -> Subst -> TypeErrorContext -> Infer (TIExpr, Subst)+inferIApplicationWithContext funcTIExpr funcType args initSubst ctx = do+  -- Infer argument types (once; shared by the main attempt and the CAS-join retry)+  argResults <- mapM (\arg -> inferIExprWithContext arg ctx) args+  let argTIExprs = map fst argResults+      argTypes = map (tiExprType . fst) argResults+      argSubst = foldr composeSubst initSubst (map snd argResults)++  -- Application-site CAS join (design/type-cas-tower.md, join table): when+  -- unifying two CAS operand types fails (in practice: closed atom sets or+  -- canonical forms that unification deliberately keeps unrelated, e.g.+  -- Poly Integer [i] vs Poly Integer [sqrt2]), compute their unique join in+  -- the declared order and retry once with every CAS argument below the+  -- join reshaped to it. Promotion is thus a coercion inserted at the+  -- application site (D5: casReshapeAs only) — the unifier itself never+  -- joins, so type errors outside the CAS order are unaffected. On retry+  -- failure the ORIGINAL mismatch is reported.+  snapshot <- saveConstraintState+  inferIApplicationUnifyPhase funcTIExpr funcType argTIExprs argTypes argSubst ctx+    `catchError` \e -> case e of+      UnificationError t1 t2 _+        | Subtype.isCasType t1, Subtype.isCasType t2 -> do+            edges <- gets inferCasSubtypeEdges+            case Subtype.joinTypesWith edges t1 t2 of+              Just j -> do+                restoreConstraintState snapshot+                let promote ti at+                      | Subtype.isCasType at, at /= j, Subtype.isSubtypeWith edges at j =+                          (TIExpr (Forall [] [] j) (TIReshape j ti), j)+                      | otherwise = (ti, at)+                    (argTIExprs', argTypes') = unzip (zipWith promote argTIExprs argTypes)+                inferIApplicationUnifyPhase funcTIExpr funcType argTIExprs' argTypes' argSubst ctx+                  `catchError` \_ -> throwError e+              Nothing -> throwError e+      _ -> throwError e++-- | The unification half of application inference: fresh parameter/result+-- variables, function-shape unification, then argument/parameter+-- unification (data arguments before callbacks). Factored out so the+-- CAS-join retry above can re-run it with reshaped arguments.+inferIApplicationUnifyPhase :: TIExpr -> Type -> [TIExpr] -> [Type] -> Subst -> TypeErrorContext -> Infer (TIExpr, Subst)+inferIApplicationUnifyPhase funcTIExpr funcType argTIExprs argTypes argSubst ctx = do+  -- Create fresh type variables for parameters and result+  paramVars <- mapM (\i -> freshVar ("param" ++ show i)) [1..length argTypes]+  resultType <- freshVar "result"+  let expectedFuncType = foldr TFun resultType paramVars+  appliedFuncType <- applySubstWithConstraintsM argSubst funcType+++  -- First unify function type structure to get parameter bindings+  let funcScheme = tiScheme funcTIExpr+      (Forall _tvs funcConstraints _) = funcScheme+  classEnv <- getClassEnv+  -- Include constraints from both the function being applied AND the inference context+  -- The context constraints include constraints from outer scopes (e.g., {Num a} from (.) definition)+  contextConstraints <- getConstraints+  let constraints = funcConstraints ++ contextConstraints+  case Unify.unifyWithConstraints classEnv constraints appliedFuncType expectedFuncType of+    Right (s1, flag1) -> do+      -- Now unify argument types with parameter types+      -- Key: Unify non-function arguments FIRST to let data types constrain type variables+      paramTypesRaw <- mapM (applySubstWithConstraintsM s1) paramVars+      let indexedArgs = zip3 [0..] argTypes paramTypesRaw++      -- Classify arguments: non-functions first, then functions+      -- A type is considered a function if it's TFun+          isArgFunction (TFun _ _) = True+          isArgFunction _ = False+          (funcArgsList, nonFuncArgsList) = partition (\(_, at, _) -> isArgFunction at) indexedArgs++      -- Unify non-function arguments first (data types like lists)+      -- IMPORTANT: Apply substitution to constraints so that constraint checking works correctly+      (s2, flag2) <- foldM (\(s, flagAcc) (_, at, pt) -> do+                     at' <- applySubstWithConstraintsM s at+                     pt' <- applySubstWithConstraintsM s pt+                     let cs' = map (applySubstConstraint s) constraints+                     case Unify.unifyWithConstraints classEnv cs' at' pt' of+                       Right (s', flag') -> return (composeSubst s' s, flagAcc || flag')+                       Left _ -> throwError $ UnificationError at' pt' ctx+                  ) (s1, flag1) nonFuncArgsList++      -- Then unify function arguments (callbacks)+      -- IMPORTANT: Include constraints from the argument's type scheme (e.g., {Num t} from (+))+      -- so that constraint checking works correctly for the argument's type variables+      (s3, flag3) <- foldM (\(s, flagAcc) (idx, at, pt) -> do+                     at' <- applySubstWithConstraintsM s at+                     pt' <- applySubstWithConstraintsM s pt+                     let -- Get constraints from both the outer function and the argument itself+                         outerCs = map (applySubstConstraint s) constraints+                         argScheme = tiScheme (argTIExprs !! idx)+                         (Forall _ argConstraints _) = argScheme+                         argCs = map (applySubstConstraint s) argConstraints+                         allCs = outerCs ++ argCs+                     case Unify.unifyWithConstraints classEnv allCs at' pt' of+                       Right (s', flag') -> return (composeSubst s' s, flagAcc || flag')+                       Left _ -> throwError $ UnificationError at' pt' ctx+                  ) (s2, flag2) funcArgsList++      let finalS = composeSubst s3 argSubst+      baseResultType <- applySubstWithConstraintsM finalS resultType++      -- Check if Tensor was unwrapped during unification (flag3)+      -- If so, wrap the result type in Tensor+      -- This handles cases like sum : {Num a} [a] -> a with [Tensor Integer]+      -- where a unifies with Tensor Integer but gets unwrapped to Integer+      let needsTensorWrap = flag3+          finalType = if needsTensorWrap && not (Types.isTensorType baseResultType)+                      then TTensor baseResultType+                      else baseResultType++      -- Apply substitution to constraints and simplify Tensor constraints+      -- This rewrites C (Tensor a) to C a when appropriate, while keeping types as Tensor a+      -- IMPORTANT: Only use funcConstraints for the result scheme, not contextConstraints+      -- contextConstraints are from outer scopes and should not be propagated to sub-expressions+      let updatedFuncConstraints = map (applySubstConstraint finalS) funcConstraints+          simplifiedFuncConstraints = simplifyTensorConstraints classEnv updatedFuncConstraints+          -- Deduplicate constraints+          deduplicatedConstraints = nub simplifiedFuncConstraints+          -- Filter out constraints on concrete types (only keep constraints on type variables)+          -- This prevents constraints like {Num (Tensor t0)} from appearing in result types+          -- Multi-param-aware: keep constraints if at least one of the type+          -- arguments is still a type variable (these still need dictionary+          -- threading at higher up).+          isTypeVarConstraint c = any isTypeVarType (constraintTypes c)+          isTypeVarType (TVar _) = True+          isTypeVarType _        = False+          typeVarConstraints = filter isTypeVarConstraint deduplicatedConstraints+          -- Result constraints: functions (partial applications) keep constraints,+          -- but values (fully applied) don't need them+          resultConstraints = case finalType of+                                TFun _ _ -> typeVarConstraints  -- Partial application+                                _ -> []  -- Fully applied: no constraints needed+          resultScheme = Forall [] resultConstraints finalType++          -- Update function and argument TIExprs+          -- IMPORTANT: Use applySubstToTIExprWithClassEnv to adjust substitution based on constraints+          -- When {Num t0} t0 -> t0 is unified with Tensor t1, if Num (Tensor t1) has no instance,+          -- the substitution is adjusted to t0 -> t1 (unwrapping the Tensor)+          updatedFuncTI = applySubstToTIExprWithClassEnv classEnv finalS funcTIExpr+          updatedArgTIs = map (applySubstToTIExprWithClassEnv classEnv finalS) argTIExprs++      return (TIExpr resultScheme (TIApplyExpr updatedFuncTI updatedArgTIs), finalS)++    Left _ ->+      -- Special case: if function has type MathValue, allow application returning MathValue+      -- (handles FunctionData application, e.g. f 0 where f := function (x))+      case appliedFuncType of+        TMathValue -> do+          classEnv' <- getClassEnv+          let resultScheme = Forall [] [] TMathValue+              updatedFuncTI = applySubstToTIExprWithClassEnv classEnv' argSubst funcTIExpr+              updatedArgTIs = map (applySubstToTIExprWithClassEnv classEnv' argSubst) argTIExprs+          return (TIExpr resultScheme (TIApplyExpr updatedFuncTI updatedArgTIs), argSubst)+        _ -> throwError $ UnificationError appliedFuncType expectedFuncType ctx+-- | Infer let bindings (non-recursive)++-- | Infer let bindings (non-recursive) with context+-- NEW: Returns TIBindingExpr instead of IBindingExpr+-- Infer IO bindings for do expressions+inferIOBindingsWithContext :: [IBindingExpr] -> TypeEnv -> Subst -> TypeErrorContext -> Infer ([TIBindingExpr], [(String, TypeScheme)], Subst)+inferIOBindingsWithContext [] _env s _ctx = return ([], [], s)+inferIOBindingsWithContext ((pat, expr):bs) env s ctx = do+  -- Infer the type of the expression+  (exprTI, s1) <- inferIExprWithContext expr ctx+  let exprType = tiExprType exprTI++  -- The expression should be of type IO a+  innerType <- freshVar "ioInner"+  exprType' <- applySubstWithConstraintsM s1 exprType+  s2 <- unifyTypesWithContext exprType' (TIO innerType) ctx+  let s12 = composeSubst s2 s1+  actualInnerType <- applySubstWithConstraintsM s12 innerType++  -- Create expected type from pattern and unify with inner type+  (patternType, s3) <- inferPatternType pat+  let s123 = composeSubst s3 s12+  actualInnerType' <- applySubstWithConstraintsM s123 actualInnerType+  patternType' <- applySubstWithConstraintsM s123 patternType+  s4 <- unifyTypesWithContext actualInnerType' patternType' ctx++  -- Apply all substitutions and extract bindings with inner type+  let finalS = composeSubst s4 s123+  finalInnerType <- applySubstWithConstraintsM finalS actualInnerType+  let bindings = extractIBindingsFromPattern pat finalInnerType+      s' = composeSubst finalS s++  _env' <- getEnv+  let extendedEnvList = bindings  -- Already a list of (String, TypeScheme)+  (restBindingTIs, restBindings, s2') <- withEnv extendedEnvList $ inferIOBindingsWithContext bs env s' ctx+  return ((pat, exprTI) : restBindingTIs, bindings ++ restBindings, s2')+  where+    -- Infer the type that a pattern expects+    inferPatternType :: IPrimitiveDataPattern -> Infer (Type, Subst)+    inferPatternType PDWildCard = do+      t <- freshVar "wild"+      return (t, emptySubst)+    inferPatternType (PDPatVar _) = do+      t <- freshVar "patvar"+      return (t, emptySubst)+    inferPatternType (PDTuplePat pats) = do+      results <- mapM inferPatternType pats+      let types = map fst results+          substs = map snd results+          s = foldr composeSubst emptySubst substs+      return (TTuple types, s)+    inferPatternType PDEmptyPat = return (TCollection (TVar (TyVar "a")), emptySubst)+    inferPatternType (PDConsPat _ _) = do+      elemType <- freshVar "elem"+      return (TCollection elemType, emptySubst)+    inferPatternType (PDSnocPat _ _) = do+      elemType <- freshVar "elem"+      return (TCollection elemType, emptySubst)+    inferPatternType (PDInductivePat name pats) = do+      results <- mapM inferPatternType pats+      let types = map fst results+          substs = map snd results+          s = foldr composeSubst emptySubst substs+      return (TInductive name types, s)+    inferPatternType (PDConstantPat c) = do+      ty <- inferConstant c+      return (ty, emptySubst)+    -- MathValue primitive patterns+    inferPatternType (PDFracPat _ _) = return (TMathValue, emptySubst)+    inferPatternType (PDPlusPat _) = return (TPolyExpr, emptySubst)+    inferPatternType (PDTermPat _ _) = return (TTermExpr, emptySubst)+    inferPatternType (PDSymbolPat _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply1Pat _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply2Pat _ _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply3Pat _ _ _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply4Pat _ _ _ _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDQuotePat _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDFunctionPat _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDSubPat _) = return (TIndexExpr, emptySubst)+    inferPatternType (PDSupPat _) = return (TIndexExpr, emptySubst)+    inferPatternType (PDUserPat _) = return (TIndexExpr, emptySubst)++-- | Apply substitution recursively until a fixed point is reached+-- This ensures that nested type variables are fully resolved+-- For example, if s = {t1 -> (Integer, t2), t2 -> [Integer]}, then+-- applySubstRecursively s t1 will return (Integer, [Integer])+-- instead of (Integer, t2)+applySubstRecursively :: Subst -> Type -> Infer Type+applySubstRecursively s t = applySubstRecursively' s t 5  -- Max 5 iterations (reduced from 10)+  where+    applySubstRecursively' :: Subst -> Type -> Int -> Infer Type+    applySubstRecursively' _ t 0 = return t  -- Stop after max iterations+    applySubstRecursively' s t n = do+      t' <- applySubstWithConstraintsM s t+      if t' == t+        then return t+        else applySubstRecursively' s t' (n - 1)++inferIBindingsWithContext :: [IBindingExpr] -> TypeEnv -> Subst -> TypeErrorContext -> Infer ([TIBindingExpr], [(String, TypeScheme)], Subst)+inferIBindingsWithContext [] _env s _ctx = return ([], [], s)+inferIBindingsWithContext ((pat, expr):bs) env s ctx = do+  -- Infer the type of the expression+  (exprTI, s1) <- inferIExprWithContext expr ctx+  let exprType = tiExprType exprTI++  -- Create expected type from pattern and unify with expression type+  -- This helps resolve type variables in the expression type+  (patternType, s2) <- inferPatternType pat+  let s12 = composeSubst s2 s1+  exprType' <- applySubstWithConstraintsM s12 exprType+  patternType' <- applySubstWithConstraintsM s12 patternType+  s3 <- unifyTypesWithContext exprType' patternType' ctx++  -- Apply all substitutions recursively until fixed point+  -- This ensures nested type variables are fully resolved (e.g., for sortWithSign)+  let finalS = composeSubst s3 s12+  finalExprType <- applySubstRecursively finalS exprType++  -- Let-generalization (paper T-LET, standard Hindley-Milner): quantify the+  -- binding over its type variables that occur neither in the environment nor+  -- in the accumulated class constraints.  The environment's free variables+  -- are zonked first: a lambda-bound variable already committed by the global+  -- substitution stands for its image's variables, which a stale entry does+  -- not mention.  Constrained variables stay monomorphic (a deliberate+  -- restriction): dictionaries are threaded at top-level definitions only, so+  -- generalizing a constrained local binding would outrun the runtime's+  -- dictionary passing.  Matcher-typed bindings (e.g. `let m := something`)+  -- are constraint-free and generalize fully, so a let-bound matcher may+  -- serve differently-typed match sites (matcher polymorphism), unlike a+  -- lambda-bound, monomorphic one.+  -- Only a single-variable binding (the paper's `let x = e1 in e2` form) is+  -- generalized; destructuring bindings keep monomorphic components.+  let rhsFree = freeTyVars finalExprType+  bindings <-+    case pat of+      PDPatVar _ | not (Set.null rhsFree) -> do+        envNow <- getEnv+        envFreeImages <- mapM (applySubstWithConstraintsM emptySubst . TVar)+                              (Set.toList (freeVarsInEnv envNow))+        constraintsNow <- getConstraints+        let envFreeZ = Set.unions (map freeTyVars envFreeImages)+            consFree = Set.unions [ freeTyVars t | c <- constraintsNow, t <- constraintTypes c ]+            genSet = rhsFree `Set.difference` (envFreeZ `Set.union` consFree)+            regeneralize (n, Forall _ _ t) =+              (n, Forall (Set.toList (freeTyVars t `Set.intersection` genSet)) [] t)+        return (map regeneralize (extractIBindingsFromPattern pat finalExprType))+      _ -> return (extractIBindingsFromPattern pat finalExprType)+  let s' = composeSubst finalS s++  _env' <- getEnv+  let extendedEnvList = bindings  -- Already a list of (String, TypeScheme)+  (restBindingTIs, restBindings, s2') <- withEnv extendedEnvList $ inferIBindingsWithContext bs env s' ctx+  return ((pat, exprTI) : restBindingTIs, bindings ++ restBindings, s2')+  where+    -- Infer the type that a pattern expects+    inferPatternType :: IPrimitiveDataPattern -> Infer (Type, Subst)+    inferPatternType PDWildCard = do+      t <- freshVar "wild"+      return (t, emptySubst)+    inferPatternType (PDPatVar _) = do+      t <- freshVar "patvar"+      return (t, emptySubst)+    inferPatternType (PDTuplePat pats) = do+      results <- mapM inferPatternType pats+      let types = map fst results+          substs = map snd results+          s = foldr composeSubst emptySubst substs+      return (TTuple types, s)+    inferPatternType PDEmptyPat = return (TCollection (TVar (TyVar "a")), emptySubst)+    inferPatternType (PDConsPat _ _) = do+      elemType <- freshVar "elem"+      return (TCollection elemType, emptySubst)+    inferPatternType (PDSnocPat _ _) = do+      elemType <- freshVar "elem"+      return (TCollection elemType, emptySubst)+    inferPatternType (PDInductivePat name pats) = do+      results <- mapM inferPatternType pats+      let types = map fst results+          substs = map snd results+          s = foldr composeSubst emptySubst substs+      return (TInductive name types, s)+    inferPatternType (PDConstantPat c) = do+      ty <- inferConstant c+      return (ty, emptySubst)+    -- MathValue primitive patterns+    inferPatternType (PDFracPat _ _) = return (TMathValue, emptySubst)+    inferPatternType (PDPlusPat _) = return (TPolyExpr, emptySubst)+    inferPatternType (PDTermPat _ _) = return (TTermExpr, emptySubst)+    inferPatternType (PDSymbolPat _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply1Pat _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply2Pat _ _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply3Pat _ _ _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDApply4Pat _ _ _ _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDQuotePat _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDFunctionPat _ _) = return (TSymbolExpr, emptySubst)+    inferPatternType (PDSubPat _) = return (TIndexExpr, emptySubst)+    inferPatternType (PDSupPat _) = return (TIndexExpr, emptySubst)+    inferPatternType (PDUserPat _) = return (TIndexExpr, emptySubst)++-- | Infer letrec bindings (recursive)++-- | Infer letrec bindings (recursive) with context+-- NEW: Returns TIBindingExpr instead of IBindingExpr+inferIRecBindingsWithContext :: [IBindingExpr] -> TypeEnv -> Subst -> TypeErrorContext -> Infer ([TIBindingExpr], [(String, TypeScheme)], Subst)+inferIRecBindingsWithContext bindings _env s ctx = do+  -- Create placeholders with fresh type variables+  placeholders <- mapM (\(pat, _) -> do+    (patternType, s1) <- inferPatternType pat+    return (pat, patternType, s1)) bindings+  +  let placeholderTypes = map (\(_, ty, _) -> ty) placeholders+      placeholderSubsts = map (\(_, _, s) -> s) placeholders+      s0 = foldr composeSubst s placeholderSubsts+  +  -- Extract bindings from placeholders+  let placeholderBindings = concat $ zipWith (\(pat, _, _) ty -> extractIBindingsFromPattern pat ty) placeholders placeholderTypes+  +  -- Infer expressions in extended environment+  results <- withEnv placeholderBindings $ mapM (\(_, expr) -> inferIExprWithContext expr ctx) bindings+  +  let exprTIs = map fst results+      exprTypes = map (tiExprType . fst) results+      substList = map snd results+      s1 = foldr composeSubst s0 substList+  +  -- Unify placeholder types with inferred expression types+  unifySubsts <- zipWithM (\placeholderTy exprTy -> do+    placeholderTy' <- applySubstWithConstraintsM s1 placeholderTy+    exprTy' <- applySubstWithConstraintsM s1 exprTy+    unifyTypesWithContext exprTy' placeholderTy' ctx) placeholderTypes exprTypes+  +  let finalS = foldr composeSubst s1 unifySubsts++  -- Re-extract bindings with fully resolved types+  exprTypes' <- mapM (applySubstRecursively finalS) exprTypes+  -- Let-generalization (paper T-LET, standard Hindley-Milner; the surface+  -- `let` parses as letrec, so this is the binding form the paper's+  -- let-generalization claim refers to).  Quantify each single-variable+  -- binding over its type variables that occur neither in the (zonked)+  -- environment nor in the accumulated class constraints:+  --   * the environment's free variables are zonked first — a lambda-bound+  --     variable already committed by the global substitution stands for its+  --     image's variables, which the stale entry does not mention;+  --   * constrained variables stay monomorphic (dictionaries are threaded at+  --     top-level definitions only, so a generalized constrained local+  --     binding would outrun the runtime's dictionary passing);+  --   * a matcher-literal binding (the desugarer wraps `matcher` definitions+  --     in a letrec) stays monomorphic: generalizing it would make the body's+  --     variable reference instantiate a fresh copy, severing the clause+  --     trees' type variables from the definition's final type.+  -- A let-bound matcher VALUE (e.g. `let m := something`) thus generalizes+  -- and may serve differently-typed match sites (matcher polymorphism),+  -- unlike a lambda-bound, monomorphic one.+  let groupFree = Set.unions (map freeTyVars exprTypes')+      monoExtract = concat $ zipWith (\(pat, _, _) ty -> extractIBindingsFromPattern pat ty) placeholders exprTypes'+  finalBindings <-+    if Set.null groupFree+      then return monoExtract+      else do+        envNow <- getEnv+        envFreeImages <- mapM (applySubstWithConstraintsM emptySubst . TVar)+                              (Set.toList (freeVarsInEnv envNow))+        constraintsNow <- getConstraints+        let envFreeZ = Set.unions (map freeTyVars envFreeImages)+            consFree = Set.unions [ freeTyVars t | c <- constraintsNow, t <- constraintTypes c ]+            genSet = groupFree `Set.difference` (envFreeZ `Set.union` consFree)+            genOne (pat, _, _) ty (_, rhs) = case (pat, rhs) of+              (PDPatVar _, IMatcherExpr _) -> extractIBindingsFromPattern pat ty+              (PDPatVar _, _) ->+                map (\(n, Forall _ _ t) ->+                       (n, Forall (Set.toList (freeTyVars t `Set.intersection` genSet)) [] t))+                    (extractIBindingsFromPattern pat ty)+              _ -> extractIBindingsFromPattern pat ty+        return (concat (zipWith3 genOne placeholders exprTypes' bindings))+  let transformedBindings = zipWith (\(pat, _) exprTI -> (pat, exprTI)) bindings exprTIs++  return (transformedBindings, finalBindings, finalS)+  where+    -- Infer the type that a pattern expects (same as in inferIBindingsWithContext)+    inferPatternType :: IPrimitiveDataPattern -> Infer (Type, Subst)+    inferPatternType PDWildCard = do+      t <- freshVar "wild"+      return (t, emptySubst)+    inferPatternType (PDPatVar _) = do+      t <- freshVar "rec"+      return (t, emptySubst)+    inferPatternType (PDTuplePat pats) = do+      results <- mapM inferPatternType pats+      let types = map fst results+          substs = map snd results+          s = foldr composeSubst emptySubst substs+      return (TTuple types, s)+    inferPatternType PDEmptyPat = return (TCollection (TVar (TyVar "a")), emptySubst)+    inferPatternType (PDConsPat _ _) = do+      elemType <- freshVar "elem"+      return (TCollection elemType, emptySubst)+    inferPatternType (PDSnocPat _ _) = do+      elemType <- freshVar "elem"+      return (TCollection elemType, emptySubst)+    inferPatternType (PDInductivePat name pats) = do+      results <- mapM inferPatternType pats+      let types = map fst results+          substs = map snd results+          s = foldr composeSubst emptySubst substs+      return (TInductive name types, s)+    inferPatternType (PDConstantPat c) = do+      ty <- inferConstant c+      return (ty, emptySubst)+    -- Add other cases as needed+    inferPatternType _ = do+      t <- freshVar "rec"+      return (t, emptySubst)++-- | Extract bindings from pattern+-- This function extracts variable bindings from a primitive data pattern+-- given the type that the pattern should match against+-- Helper to check if a pattern is a pattern variable+isPatVarPat :: IPrimitiveDataPattern -> Bool+isPatVarPat (PDPatVar _) = True+isPatVarPat _ = False++extractIBindingsFromPattern :: IPrimitiveDataPattern -> Type -> [(String, TypeScheme)]+extractIBindingsFromPattern pat ty = case pat of+  PDWildCard -> []+  PDPatVar var -> [(extractNameFromVar var, Forall [] [] ty)]+  PDInductivePat _ pats -> concatMap (\p -> extractIBindingsFromPattern p ty) pats+  PDTuplePat pats -> +    case ty of+      TTuple tys | length pats == length tys -> +        -- Types match: bind each pattern variable to corresponding type+        concat $ zipWith extractIBindingsFromPattern pats tys+      _ -> +        -- Type is not a resolved tuple (might be type variable or mismatch)+        -- Extract pattern variables but assign them the full tuple type for now+        -- This is imprecise but allows variables to be in scope+        -- The actual element types will be determined during later unification+        concatMap (\p -> extractIBindingsFromPattern p ty) pats+  PDEmptyPat -> []+  PDConsPat p1 p2 ->+    case ty of+      TCollection elemTy -> extractIBindingsFromPattern p1 elemTy ++ extractIBindingsFromPattern p2 ty+      _ -> []+  PDSnocPat p1 p2 ->+    case ty of+      TCollection elemTy -> extractIBindingsFromPattern p1 ty ++ extractIBindingsFromPattern p2 elemTy+      _ -> []+  -- MathValue primitive patterns+  PDFracPat p1 p2 ->+    let polyExprTy = TPolyExpr+        mathValueTy = TMathValue+        p1Ty = if isPatVarPat p1 then mathValueTy else polyExprTy+        p2Ty = if isPatVarPat p2 then mathValueTy else polyExprTy+    in extractIBindingsFromPattern p1 p1Ty ++ extractIBindingsFromPattern p2 p2Ty+  PDPlusPat p ->+    let termExprTy = TTermExpr+        mathValueTy = TMathValue+        pTy = if isPatVarPat p then TCollection mathValueTy else TCollection termExprTy+    in extractIBindingsFromPattern p pTy+  PDTermPat p1 p2 ->+    let symbolExprTy = TSymbolExpr+        mathValueTy = TMathValue+        p2Ty = if isPatVarPat p2+               then TCollection (TTuple [mathValueTy, TInt])+               else TCollection (TTuple [symbolExprTy, TInt])+    in extractIBindingsFromPattern p1 TInt ++ extractIBindingsFromPattern p2 p2Ty+  PDSymbolPat p1 p2 ->+    let indexExprTy = TIndexExpr+    in extractIBindingsFromPattern p1 TString ++ extractIBindingsFromPattern p2 (TCollection indexExprTy)+  PDApply1Pat p1 p2 ->+    let mathValueTy = TMathValue+        fnTy = TFun mathValueTy mathValueTy+    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathValueTy+  PDApply2Pat p1 p2 p3 ->+    let mathValueTy = TMathValue+        fnTy = TFun mathValueTy (TFun mathValueTy mathValueTy)+    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathValueTy ++ extractIBindingsFromPattern p3 mathValueTy+  PDApply3Pat p1 p2 p3 p4 ->+    let mathValueTy = TMathValue+        fnTy = TFun mathValueTy (TFun mathValueTy (TFun mathValueTy mathValueTy))+    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathValueTy ++ extractIBindingsFromPattern p3 mathValueTy ++ extractIBindingsFromPattern p4 mathValueTy+  PDApply4Pat p1 p2 p3 p4 p5 ->+    let mathValueTy = TMathValue+        fnTy = TFun mathValueTy (TFun mathValueTy (TFun mathValueTy (TFun mathValueTy mathValueTy)))+    in extractIBindingsFromPattern p1 fnTy ++ extractIBindingsFromPattern p2 mathValueTy ++ extractIBindingsFromPattern p3 mathValueTy ++ extractIBindingsFromPattern p4 mathValueTy ++ extractIBindingsFromPattern p5 mathValueTy+  PDQuotePat p ->+    let mathValueTy = TMathValue+    in extractIBindingsFromPattern p mathValueTy+  PDFunctionPat p1 p2 ->+    let mathValueTy = TMathValue+    in extractIBindingsFromPattern p1 mathValueTy ++ extractIBindingsFromPattern p2 (TCollection mathValueTy)+  PDSubPat p ->+    let mathValueTy = TMathValue+    in extractIBindingsFromPattern p mathValueTy+  PDSupPat p ->+    let mathValueTy = TMathValue+    in extractIBindingsFromPattern p mathValueTy+  PDUserPat p ->+    let mathValueTy = TMathValue+    in extractIBindingsFromPattern p mathValueTy+  _ -> []++-- | Infer top-level IExpr and return TITopExpr directly+-- | Warn when a top-level definition reuses a class method name.  Such a+-- def replaces the dispatching binding for the rest of the program, which+-- surfaces as baffling type errors far from the definition.  Class and+-- instance declarations lower to IDefineMany (registry, wrappers,+-- dictionaries), never to an IDefine of the bare method name, so a name+-- match at IDefine is always user shadowing.+warnOnClassMethodShadow :: Var -> Infer ()+warnOnClassMethodShadow (Var defName _) = do+  classEnv <- getClassEnv+  case [ cls | (cls, info) <- classEnvToList classEnv+             , defName `elem` map fst (Types.classMethods info) ] of+    (cls:_) -> addWarning (ClassMethodShadowWarning defName cls emptyContext)+    []      -> return ()++inferITopExpr :: ITopExpr -> Infer (Maybe TITopExpr, Subst)+inferITopExpr topExpr = case topExpr of+  IDefine var expr -> do+    warnOnClassMethodShadow var+    env <- getEnv+    -- Check if there's an explicit type signature in the environment+    -- (added by EnvBuilder from DefineWithType)+    case lookupEnv var env of+      Just existingScheme -> do+        -- There's an explicit type signature: check that the inferred type matches+        st <- get+        classEnv <- getClassEnv+        let (instConstraints0, expectedType, newCounter) = instantiate existingScheme (inferCounter st)+            -- Expand superclass constraints so that superclass methods are available+            -- e.g., {Ord a} -> {Ord a, Eq a} since Ord extends Eq+            instConstraints = expandSuperclasses classEnv instConstraints0+        modify $ \s -> s { inferCounter = newCounter }+        -- Add instantiated constraints to the inference context+        -- This is crucial for constraint-aware unification inside the definition body+        -- e.g., when (.) has {Num a}, this constraint must be visible when type-checking t1 * t2+        clearConstraints  -- Start fresh+        clearDeferredHoleChecks+        addConstraints instConstraints++        -- Infer the expression type+        (exprTI, subst1) <- inferIExpr expr+        let exprType = tiExprType exprTI++        -- Unify inferred type with expected type using constraint-aware unification+        -- This is crucial for cases like (.) where type variables have constraints+        -- The constraints from the type signature affect how Tensor types are unified+        let exprCtx = withExpr (prettyStr expr) emptyContext+            -- Apply substitution to constraints to get current state+            currentConstraints = map (applySubstConstraint subst1) instConstraints+        exprType' <- applySubstWithConstraintsM subst1 exprType+        expectedType' <- applySubstWithConstraintsM subst1 expectedType+        -- Matcher-rigidity exception: a matcher LITERAL (possibly behind the+        -- lambdas of a parameterized definition) checked against its+        -- annotation (paper T-MATCHER's checking mode).  The literal's+        -- structural capability is derived by the clause checks at whatever+        -- type the annotation names, so unifying the Matcher parameters at+        -- the result position is sound; rigidity guards already-existing+        -- matcher values, not the literal being defined.+        subst2 <- case rhsCore expr of+          IMatcherExpr _ ->+            unifyMatcherDefType currentConstraints exprType' expectedType' exprCtx+          _ -> unifyTypesWithConstraints currentConstraints exprType' expectedType' exprCtx+        let finalSubst = composeSubst subst2 subst1++        -- Reject the definition if its body needs constraints (on the+        -- signature's type variables) that the signature does not declare+        finalTypeChk <- applySubstWithConstraintsM finalSubst expectedType+        let Var defNameStr _ = var+        checkResidualConstraints defNameStr instConstraints finalTypeChk finalSubst exprCtx++        -- Deferred matcher-hole admissibility (paper PP-Con) at the final types+        flushDeferredHoleChecks finalSubst++        -- Apply final substitution to exprTI to resolve all type variables+        -- IMPORTANT: Use applySubstToTIExprM to adjust substitution based on constraints+        exprTI' <- applySubstToTIExprM finalSubst exprTI++        -- Resolve constraints in exprTI' (Tensor t0 -> t0)+        classEnv <- getClassEnv+        let exprTI'' = resolveConstraintsInTIExpr classEnv finalSubst exprTI'+        +        -- Reconstruct type scheme from exprTI'' to match actual type variables+        -- Use instantiated constraints and apply final substitution+        -- When there's an explicit type annotation, use the expected type+        -- (with substitutions applied) as the final type, not the inferred type.+        -- This ensures that Tensor types are preserved when explicitly annotated.+        finalType <- applySubstWithConstraintsM finalSubst expectedType+        let constraints' = map (applySubstConstraint finalSubst) instConstraints+            envFreeVars = freeVarsInEnv env+            typeFreeVars = freeTyVars finalType+            genVars = Set.toList $ typeFreeVars `Set.difference` envFreeVars+            updatedScheme = Forall genVars constraints' finalType+        +        -- Update the environment with the expanded scheme+        -- This is important so that call sites see the full constraints+        -- (including superclass-expanded ones) and pass all needed dictionaries+        modify $ \s -> s { inferEnv = extendEnv var updatedScheme (inferEnv s) }+        return (Just (TIDefine updatedScheme var exprTI''), finalSubst)+      +      Nothing -> do+        -- No explicit type signature: infer and generalize as before+        clearConstraints  -- Start with fresh constraints for this expression+        clearDeferredHoleChecks+        -- Monomorphic recursion: bind the definition's own name to a fresh+        -- type variable before inferring the body, so recursive calls+        -- constrain it.  (Previously the name was unbound in its own body:+        -- the calls warned, fell to Any, and could fail at runtime.  The+        -- accident that hid this: a def shadowing a class method looked+        -- its own name up in the METHOD's scheme.)  The body's type is+        -- unified with the placeholder below, so polymorphic recursion+        -- still needs an explicit signature, as in ML.+        selfTy <- freshVar "rec"+        modify $ \s -> s { inferEnv = extendEnv var (Forall [] [] selfTy) (inferEnv s) }+        (exprTI, subst1) <- inferIExpr expr+        -- Deferred matcher-hole admissibility (paper PP-Con) at the final types+        flushDeferredHoleChecks subst1+        -- Apply the substitution to the stored expression, exactly as the+        -- signature branch does.  Without this, node schemes inside the+        -- expression (notably matcher data-clause arms) keep stale type+        -- variables in their constraints, and the generalized scheme below+        -- is built from variables that no longer match the nodes —+        -- TypeClassExpand then emits unbound dictionary references.+        exprTI0 <- applySubstToTIExprM subst1 exprTI+        -- Tie the placeholder to the inferred body type.  For a+        -- non-recursive body the placeholder is still free and the+        -- unification just discharges it; for a recursive one this is+        -- where the recursive uses meet the definition.+        selfTy' <- applySubstWithConstraintsM subst1 selfTy+        let Var selfName _ = var+            recCtx = withContext+              ("in the definition of '" ++ selfName +++               "': the type of a recursive use does not match the body" +++               " (polymorphic recursion needs an explicit type signature)")+              (withExpr (prettyStr expr) emptyContext)+        subst2 <- unifyTypesWithContext selfTy' (tiExprType exprTI0) recCtx+        let subst = composeSubst subst2 subst1+        exprTI' <- applySubstToTIExprM subst2 exprTI0+        let exprType = tiExprType exprTI'+        constraints <- getConstraints  -- Collect constraints from type inference++        -- Resolve constraints based on available instances+        classEnv <- getClassEnv+        let exprTI'' = resolveConstraintsInTIExpr classEnv subst exprTI'+            updatedConstraints = map (resolveConstraintWithInstances classEnv subst) constraints+            -- Filter out constraints on concrete types (non-type-variables)+            -- Concrete constraints don't need to be generalized since the type is already determined+            isTypeVarConstraint c = any isTypeVarType' (constraintTypes c)+            isTypeVarType' (TVar _) = True+            isTypeVarType' _        = False+            -- Deduplicate constraints (e.g., {Num a, Num a} -> {Num a})+            generalizedConstraints = nub $ filter isTypeVarConstraint updatedConstraints++        -- Generalize with filtered constraints (only type variables)+        let envFreeVars = freeVarsInEnv env+            typeFreeVars = freeTyVars exprType+            genVars = Set.toList $ typeFreeVars `Set.difference` envFreeVars+            scheme = Forall genVars generalizedConstraints exprType++        -- Add to environment using the Var directly (preserves index info)+        modify $ \s -> s { inferEnv = extendEnv var scheme (inferEnv s) }++        return (Just (TIDefine scheme var exprTI''), subst)+  +  ITest expr -> do+    clearConstraints  -- Start with fresh constraints+    clearDeferredHoleChecks+    (exprTI, subst) <- inferIExpr expr+    flushDeferredHoleChecks subst+    -- Constraints are now in state, will be retrieved by Eval.hs+    return (Just (TITest exprTI), subst)+  +  IExecute expr -> do+    clearConstraints  -- Start with fresh constraints+    clearDeferredHoleChecks+    (exprTI, subst) <- inferIExpr expr+    flushDeferredHoleChecks subst+    -- Constraints are now in state, will be retrieved by Eval.hs+    return (Just (TIExecute exprTI), subst)+  +  ILoadFile _path -> return (Nothing, emptySubst)+  ILoad _lib -> return (Nothing, emptySubst)++  IDefineMany bindings -> do+    -- Process each binding in the list+    env <- getEnv+    results <- mapM (inferBinding env) bindings+    let bindingsTI = map fst results+        substs = map snd results+        combinedSubst = foldr composeSubst emptySubst substs+    return (Just (TIDefineMany bindingsTI), combinedSubst)+    where+      -- An IDefineMany hash-literal binding is, by construction, a type-class+      -- instance dictionary (Desugar's makeDictDef; the other IDefineMany+      -- producers bind lambdas).  A dictionary is a heterogeneous record:+      -- method entries have the methods' own types (e.g. Ord's+      -- compare : a -> a -> Ordering next to (<) : a -> a -> Bool) and+      -- __super_ entries hold superclass dictionaries.  It is consumed through+      -- type-class expansion (dictionary passing / runtime dispatch), never+      -- through the hash's value type, so the entry types are deliberately not+      -- unified with each other — only keys are checked (String) — and the+      -- node is typed Hash String v, v fresh.  (EnvBuilder registers the+      -- dictionary's env scheme separately, approximating the value type by+      -- the first method's; we leave that scheme as is and skip checking the+      -- literal against it.)+      inferBinding _env (var, IHashExpr pairs@(_:_)) = do+        clearConstraints+        clearDeferredHoleChecks+        (pairTIs, s) <- foldM dictPair ([], emptySubst) pairs+        v <- freshVar "dictVal"+        let exprTI = TIExpr (Forall [] [] (THash TString v)) (TIHashExpr (reverse pairTIs))+        exprTI' <- applySubstToTIExprM s exprTI+        return ((var, exprTI'), s)+        where+          dictPair (acc, s) (k, vE) = do+            (kTI, s1) <- inferIExprWithContext k emptyContext+            sk <- unifyTypesWithContext (tiExprType kTI) TString emptyContext+            (vTI, s2) <- inferIExprWithContext vE emptyContext+            return ((kTI, vTI) : acc, foldr composeSubst s [s2, sk, s1])+      inferBinding env (var, expr) = do+        -- Check if there's an existing type signature+        case lookupEnv var env of+          Just existingScheme -> do+            -- With type signature: check type+            st <- get+            classEnvForSig <- getClassEnv+            let (instCs0, expectedType, newCounter) = instantiate existingScheme (inferCounter st)+                instCsMany = expandSuperclasses classEnvForSig instCs0+            modify $ \s -> s { inferCounter = newCounter }++            clearConstraints+            clearDeferredHoleChecks+            -- Make the signature's constraints visible while checking the+            -- body (parity with the IDefine signature branch)+            addConstraints instCsMany+            (exprTI, subst1) <- inferIExpr expr+            let exprType = tiExprType exprTI+            exprType' <- applySubstWithConstraintsM subst1 exprType+            expectedType' <- applySubstWithConstraintsM subst1 expectedType+            -- Matcher-rigidity exception for an annotated matcher literal,+            -- possibly lambda-wrapped (see the IDefine signature branch)+            subst2 <- case rhsCore expr of+              IMatcherExpr _ ->+                unifyMatcherDefType [] exprType' expectedType' emptyContext+              _ -> unifyTypesWithTopLevel exprType' expectedType' emptyContext+            let finalSubst = composeSubst subst2 subst1+            -- Reject if the body needs constraints the signature lacks+            finalTypeChk <- applySubstWithConstraintsM finalSubst expectedType+            let Var defNameStr _ = var+            checkResidualConstraints defNameStr instCsMany finalTypeChk finalSubst emptyContext+            flushDeferredHoleChecks finalSubst+            exprTI' <- applySubstToTIExprM finalSubst exprTI+            return ((var, exprTI'), finalSubst)+          +          Nothing -> do+            -- Without type signature: infer and generalize+            clearConstraints+            clearDeferredHoleChecks+            (exprTI, subst) <- inferIExpr expr+            flushDeferredHoleChecks subst+            -- Apply the substitution to the stored expression (same as the+            -- IDefine no-signature branch): node schemes must not keep stale+            -- type variables in their constraints, or TypeClassExpand emits+            -- unbound dictionary references (e.g. inside `declare rule`+            -- generated bodies, which arrive here via IDefineMany).+            exprTI' <- applySubstToTIExprM subst exprTI+            let exprType = tiExprType exprTI'+            constraints <- getConstraints++            -- Resolve constraints based on available instances+            classEnv <- getClassEnv+            let exprTI'' = resolveConstraintsInTIExpr classEnv subst exprTI'+                updatedConstraints = map (resolveConstraintWithInstances classEnv subst) constraints+                -- Filter out constraints on concrete types (non-type-variables)+                isTypeVarConstraint c = any isTypeVarT (constraintTypes c)+                isTypeVarT (TVar _) = True+                isTypeVarT _        = False+                -- Deduplicate constraints (e.g., {Num a, Num a} -> {Num a})+                generalizedConstraints = nub $ filter isTypeVarConstraint updatedConstraints++            -- Generalize the type+            let envFreeVars = freeVarsInEnv env+                typeFreeVars = freeTyVars exprType+                genVars = Set.toList $ typeFreeVars `Set.difference` envFreeVars+                scheme = Forall genVars generalizedConstraints exprType++            -- Add to environment for subsequent bindings using Var directly+            modify $ \s -> s { inferEnv = extendEnv var scheme (inferEnv s) }++            return ((var, exprTI''), subst)+  +  IPatternFunctionDecl name tyVars params retType body -> do+    -- Pattern function type checking (paper PATFUN-DEF):+    -- 1. Linearity side condition: each parameter occurs exactly once in the+    --    body, in declaration order+    -- 2. Add parameters to environment for type checking+    -- 3. Infer body pattern with expected return type, giving each parameter a+    --    fresh structural index beta_i and capturing the body's structural index+    -- 4. Create type scheme with type parameters, and record the structural+    --    signature beta_1 -> ... -> beta_k -> tau_p_body for PAT-APP++    clearConstraints  -- Start fresh++    let ctx = TypeErrorContext+                { errorLocation = Nothing+                , errorExpr = Just ("Pattern function: " ++ name)+                , errorContext = Just ("Expected type: " ++ show retType)+                }+        paramNames = map fst params+        paramUses = filter (`elem` paramNames) (patternVarRefsInOrder body)+    -- Linearity (PATFUN-DEF side condition): exactly one use of each parameter,+    -- in declaration order.  This is what lets MS-MNODE-VARPAT expand each+    -- argument pattern exactly once, left to right, so argument bindings appear+    -- exactly as promised and value patterns in later arguments can refer to+    -- variables bound by earlier ones.+    when (paramUses /= paramNames) $+      throwError $ TE.PatternFunctionLinearityError name paramNames paramUses ctx+    -- A parameter under an or-, loop-, not-, or forall-pattern may be expanded+    -- zero or several times along a matching path even when it occurs exactly+    -- once syntactically, so it is rejected as well.+    let branchedUses = filter (`elem` paramNames) (patternVarRefsUnderBranch body)+    when (not (null branchedUses)) $+      throwError $ TE.PatternFunctionParamUnderBranchError name branchedUses ctx++    -- Add parameters to environment for type checking the body+    -- Note: Parameter types don't need Pattern wrapper (design/pattern.md)+    let paramBindings = map (\(pname, pty) -> (pname, Forall [] [] pty)) params+    withEnv paramBindings $ do+      -- Structural side: a fresh structural index beta_i per parameter; the+      -- ~param embeddings in the body return it (IVarPat), so the body's+      -- structural index records where each argument's structure flows.+      betas <- mapM (\_ -> freshVar "beta") params+      let paramTaupMap = Map.fromList (zip paramNames betas)+      oldParamTaups <- inferPatfunParamTaup <$> get+      oldTaupEqs <- inferPatfunTaupEqs <$> get+      modify $ \z -> z { inferPatfunParamTaup = paramTaupMap, inferPatfunTaupEqs = Just [] }+      bodyResult <- (Right <$> inferIPattern body retType ctx) `catchError` (return . Left)+      taupEqs <- (fromMaybe [] . inferPatfunTaupEqs) <$> get+      modify $ \z -> z { inferPatfunParamTaup = oldParamTaups, inferPatfunTaupEqs = oldTaupEqs }+      (tiBody, _bodyBindings, subst, bodyTaup) <- either throwError return bodyResult++      -- Note: Pattern variables that reference parameters (using ~param) will appear in bodyBindings+      -- but they are NOT conflicts - they are references to the parameters themselves.+      -- Only NEW variable bindings (using $var) would be actual conflicts.+      -- Since the pattern body uses ~p1 and ~p2 (pattern variable references),+      -- not $p1 and $p2 (new bindings), we don't need to check for conflicts here.+      -- The existing semantics already handle this correctly during pattern matching.++      -- Create type scheme with type parameters+      -- Pattern function type: param1 -> param2 -> ... -> retType+      let paramTypes = map snd params+          funcType = foldr TFun retType paramTypes+          typeScheme = Forall tyVars [] funcType++      -- Structural signature beta_1 -> ... -> beta_k -> tau_p_body.  The body's+      -- node-local structural solvers keep only each node's result type, so the+      -- recorded equations are re-solved jointly here and the solution applied+      -- to the signature — recovering the links between the beta_i and the body+      -- skeleton (e.g. pair's beta_1 at the element position of its cons body).+      -- Generalized over all its variables so every application site+      -- instantiates it freshly (PAT-APP, independent of the target side).+      let structSig0 = foldr TFun bodyTaup betas+      structSig <- withIsolatedConstraints $+        (do sEq <- foldM (\acc (x, y) -> do+                       x' <- applySubstWithConstraintsM acc x+                       y' <- applySubstWithConstraintsM acc y+                       s' <- unifyTypesWithContext x' y' ctx+                       return (composeSubst s' acc)) emptySubst taupEqs+            applySubstWithConstraintsM sEq structSig0)+          `catchError` \_ -> return structSig0+      let structScheme = Forall (Set.toList (freeTyVars structSig)) [] structSig++      -- Add pattern function to inferPatternFuncEnv, inferEnv, and the+      -- structural-signature environment+      -- This allows the type checker to recognize it in subsequent declarations+      modify $ \s -> s {+        inferPatternFuncEnv = extendPatternEnv name typeScheme (inferPatternFuncEnv s),+        inferEnv = extendEnv (stringToVar name) typeScheme (inferEnv s),+        inferPatternFuncStructEnv = extendPatternEnv name structScheme (inferPatternFuncStructEnv s)+      }++      return (Just (TIPatternFunctionDecl name typeScheme params retType tiBody), subst)+  +  IDeclareSymbol names mType -> do+    -- Register declared symbols with their types+    let ty = case mType of+               Just t  -> t+               Nothing -> TInt  -- Default to Integer (MathValue)     -- Add symbols to declared symbols map     modify $ \s -> s { declaredSymbols =                          foldr (\name m -> Map.insert name ty m) 
hs-src/Language/Egison/Type/Instance.hs view
@@ -7,22 +7,124 @@  module Language.Egison.Type.Instance   ( findMatchingInstanceForType+  , findMatchingInstanceForTypes+  , findMostSpecificInstanceForTypes+  , selectMostSpecific+  , AmbiguityError(..)   ) where -import           Language.Egison.Type.Types (Type(..), TyVar(..), InstanceInfo(..), freeTyVars)+import qualified Data.Set                   as Set+import           Language.Egison.Type.Types (Type, InstanceInfo, instType, instTypes,+                                              freeTyVars) import           Language.Egison.Type.Unify (unifyStrict)+import           Language.Egison.Type.Subst (Subst, emptySubst, composeSubst, applySubst)+import           Language.Egison.Type.Subtype (SubtypeEnv, isSubtypeWith, sameCasHead) --- | Find a matching instance for a given target type--- This searches through a list of instances and returns the first one that unifies with the target type--- Used by both type inference (Infer.hs) and type class expansion (TypeClassExpand.hs)--- IMPORTANT: Uses unifyStrict to ensure Tensor a does NOT unify with a--- This prevents incorrectly matching scalar instances as tensor instances+-- | Single-type lookup. Used by sites that intentionally check only the+-- principal class type (e.g. Tensor unwrapping in `Type/Infer.hs`).+-- Type-class dispatch through `Type/TypeClassExpand.hs` uses the multi-type+-- variant `findMatchingInstanceForTypes` instead.+-- IMPORTANT: uses `unifyStrict` so `Tensor a` does NOT unify with `a`. findMatchingInstanceForType :: Type -> [InstanceInfo] -> Maybe InstanceInfo-findMatchingInstanceForType targetType instances = go instances+findMatchingInstanceForType targetType = go   where     go [] = Nothing-    go (inst:rest) =-      -- Try to unify the instance type with the target type using strict unification-      case unifyStrict (instType inst) targetType of-        Right _ -> Just inst  -- Successfully unified-        Left _  -> go rest    -- Unification failed, try next instance+    go (inst:rest) = case unifyStrict (instType inst) targetType of+      Right _ -> Just inst+      Left _  -> go rest++-- | Multi-parameter dispatch (e.g. `Coerce a b`). Each target type is+-- unified pairwise with the corresponding instance type, and substitutions+-- are composed across pairs so that repeated type variables in the instance+-- (e.g. `instance Coerce a a`) are required to unify with identical targets.+-- Single-param classes pass `[t]` and get the same result as+-- `findMatchingInstanceForType t`.+findMatchingInstanceForTypes :: [Type] -> [InstanceInfo] -> Maybe InstanceInfo+findMatchingInstanceForTypes targetTypes = go+  where+    go [] = Nothing+    go (inst:rest)+      | length (instTypes inst) /= length targetTypes = go rest+      | otherwise = case unifyAll emptySubst (zip (instTypes inst) targetTypes) of+          Right _ -> Just inst+          Left _  -> go rest++    -- Carry substitution forward so consistent type variables across pairs+    -- are enforced (e.g. `[a, a]` against `[Int, Float]` fails).+    unifyAll :: Subst -> [(Type, Type)] -> Either String Subst+    unifyAll s [] = Right s+    unifyAll s ((it, tt) : rs) =+      case unifyStrict (applySubst s it) (applySubst s tt) of+        Right s' -> unifyAll (s' `composeSubst` s) rs+        Left e   -> Left (show e)++-- | Errors raised by most-specific instance selection.+-- See design/runtime-type-dispatch.md §4 for the full rationale.+data AmbiguityError+  = NoMatchingInstance+    -- ^ No candidate instance accepts the target type.+  | AmbiguousIncomparable+    -- ^ Multiple candidates exist and at least two are not comparable+    -- via the subtype partial order, so no unique most-specific exists.+  | AmbiguousMultipleMaxima+    -- ^ Multiple most-specific candidates remain (order-equivalent+    -- types — mutual embeddings at the MathValue-coefficient level)+    -- and the representation-head tie-break could not single one out.+  deriving (Eq, Show)++-- | Most-specific selection among candidates that are already known to+-- accept the targets. `proj` projects a candidate to its type list.+--+-- The most specific candidate is the one whose projected list is+-- pointwise below every other's in the declared CAS order (complete+-- skeleton plus `declare cas-subtype` edges), so user-declared+-- embeddings participate in dispatch the same way built-in tower+-- embeddings do. Among order-EQUIVALENT survivors (the same value set+-- in different canonical forms, e.g. Poly MathValue [..] vs+-- Frac MathValue) the tie is broken by the targets' representation+-- heads — per the design principle, types select representations, so+-- dispatch follows the target's own head.+selectMostSpecific :: SubtypeEnv -> (c -> [Type]) -> [Type] -> [c] -> Either AmbiguityError c+selectMostSpecific edges proj targets cands =+  case cands of+    []  -> Left NoMatchingInstance+    [x] -> Right x+    cs  ->+      let leqList xs ys = length xs == length ys+                          && and (zipWith (isSubtypeWith edges) xs ys)+          isMostSpecific x =+            all (\y -> proj y == proj x || leqList (proj x) (proj y)) cs+          sameHeads x = length (proj x) == length targets+                        && and (zipWith sameCasHead targets (proj x))+      in case filter isMostSpecific cs of+           [unique] -> Right unique+           []       -> Left AmbiguousIncomparable+           several  ->+             case filter sameHeads several of+               [unique] -> Right unique+               _        -> Left AmbiguousMultipleMaxima++-- | A target is compatible with an instance type. The check is mode-aware:+-- if the instance type has free type variables (e.g. `Eq a`, `Coerce a [a]`),+-- we fall back to strict unification so type-variable instances can match.+-- Otherwise the instance is fully concrete and we require the target to be+-- a subtype of it (no cross-CAS-type slippage from unifyStrict's broad rules).+isCompatible :: SubtypeEnv -> Type -> Type -> Bool+isCompatible edges target inst+  | not (Set.null (freeTyVars inst)) = case unifyStrict inst target of+      Right _ -> True+      Left _  -> False+  | otherwise = isSubtypeWith edges target inst++-- | Pick the most specific instance accepting the targets (pointwise).+-- Single-param classes pass `[t]`. An instance is a candidate iff each+-- target is compatible with the corresponding instance type+-- ('isCompatible'); selection is 'selectMostSpecific'.+findMostSpecificInstanceForTypes :: SubtypeEnv -> [Type] -> [InstanceInfo] -> Either AmbiguityError InstanceInfo+findMostSpecificInstanceForTypes edges targets insts =+  selectMostSpecific edges instTypes targets+    (filter (isCompatibleAll targets . instTypes) insts)+  where+    isCompatibleAll ts is+      | length ts /= length is = False+      | otherwise = and (zipWith (isCompatible edges) ts is)
hs-src/Language/Egison/Type/Pretty.hs view
@@ -15,16 +15,16 @@  import           Data.List                  (intercalate) -import           Language.Egison.AST        (TypeExpr (..))+import           Language.Egison.AST        (TypeExpr (..), SymbolSetExpr(..), TypeAtomExpr(..)) import           Language.Egison.Type.Types (Constraint(..)) import           Language.Egison.Type.Index (Index (..), IndexKind (..)) import           Language.Egison.Type.Types (ShapeDimType (..), TensorShape (..), TyVar (..), Type (..),-                                             TypeScheme (..))+                                             TypeScheme (..), SymbolSet(..), prettyTypeAtomValue)  -- | Pretty print a Type prettyType :: Type -> String prettyType TInt             = "Integer"-prettyType TMathExpr        = "MathExpr"+prettyType TMathValue        = "MathValue" prettyType TPolyExpr        = "PolyExpr" prettyType TTermExpr        = "TermExpr" prettyType TSymbolExpr      = "SymbolExpr"@@ -46,6 +46,7 @@     prettyHashValueType t@(TFun _ _) = "(" ++ prettyType t ++ ")"     prettyHashValueType t            = prettyTypeAtom t prettyType (TMatcher t)     = "Matcher " ++ prettyTypeAtom t+prettyType (TMatcherSlot s t) = "MatcherSlot " ++ prettyTypeAtom s ++ " " ++ prettyTypeAtom t prettyType (TFun t1 t2)     = prettyTypeArg t1 ++ " -> " ++ prettyType t2   where     prettyTypeArg t@(TFun _ _) = "(" ++ prettyType t ++ ")"@@ -54,11 +55,22 @@ prettyType (TIORef t)       = "IORef " ++ prettyTypeAtom t prettyType TPort            = "Port" prettyType TAny             = "_"+-- New CAS types+prettyType TFactor          = "Factor"+prettyType (TTerm t ss)      = "Term " ++ prettyTypeAtom t ++ " " ++ prettySymbolSet ss+prettyType (TFrac t)         = "Frac " ++ prettyTypeAtom t+prettyType (TPoly t ss)     = "Poly " ++ prettyTypeAtom t ++ " " ++ prettySymbolSet ss +-- | Pretty print a SymbolSet+prettySymbolSet :: SymbolSet -> String+prettySymbolSet (SymbolSetClosed syms) = "[" ++ intercalate ", " (map prettyTypeAtomValue syms) ++ "]"+prettySymbolSet SymbolSetOpen          = "[..]"+prettySymbolSet (SymbolSetVar (TyVar v)) = v+ -- | Pretty print an atomic type (with parentheses if needed) prettyTypeAtom :: Type -> String prettyTypeAtom t@TInt       = prettyType t-prettyTypeAtom t@TMathExpr  = prettyType t+prettyTypeAtom t@TMathValue  = prettyType t prettyTypeAtom t@TPolyExpr  = prettyType t prettyTypeAtom t@TTermExpr  = prettyType t prettyTypeAtom t@TSymbolExpr = prettyType t@@ -73,6 +85,7 @@ prettyTypeAtom t@(TCollection _) = prettyType t prettyTypeAtom t@TPort       = prettyType t prettyTypeAtom t@TAny        = prettyType t+prettyTypeAtom t@TFactor     = prettyType t prettyTypeAtom t            = "(" ++ prettyType t ++ ")"  -- | Pretty print a TypeScheme@@ -82,15 +95,9 @@   prettyConstraintsAlt cs ++ " " ++ prettyType t prettyTypeScheme (Forall vs [] t) =   "∀" ++ unwords (map (\(TyVar v) -> v) vs) ++ ". " ++ prettyType t-prettyTypeScheme (Forall vs cs t) =+prettyTypeScheme (Forall _vs cs t) =   prettyConstraintsAlt cs ++ " " ++ prettyType t --- | Pretty print constraints (old format: "Eq a, Ord b")-prettyConstraints :: [Constraint] -> String-prettyConstraints []  = ""-prettyConstraints [c] = prettyConstraint c-prettyConstraints cs  = "(" ++ intercalate ", " (map prettyConstraint cs) ++ ")"- -- | Pretty print constraints (new format: "{Eq a, Ord b}") prettyConstraintsAlt :: [Constraint] -> String prettyConstraintsAlt []  = ""@@ -98,7 +105,7 @@  -- | Pretty print a single constraint prettyConstraint :: Constraint -> String-prettyConstraint (Constraint cls ty) = cls ++ " " ++ prettyTypeAtom ty+prettyConstraint (Constraint cls tys) = cls ++ concatMap (\t -> " " ++ prettyTypeAtom t) tys  -- | Pretty print a TensorShape prettyTensorShape :: TensorShape -> String@@ -123,7 +130,7 @@ -- | Pretty print a TypeExpr (source-level type) prettyTypeExpr :: TypeExpr -> String prettyTypeExpr TEInt          = "Integer"-prettyTypeExpr TEMathExpr     = "MathExpr"+prettyTypeExpr TEMathValue     = "MathValue" prettyTypeExpr TEFloat        = "Float" prettyTypeExpr TEBool         = "Bool" prettyTypeExpr TEChar         = "Char"@@ -137,15 +144,41 @@     prettyTypeExprArg t@(TEFun _ _) = "(" ++ prettyTypeExpr t ++ ")"     prettyTypeExprArg t             = prettyTypeExpr t prettyTypeExpr (TEMatcher t)  = "Matcher " ++ prettyTypeExprAtom t+prettyTypeExpr (TEMatcherSlot s t) = "MatcherSlot " ++ prettyTypeExprAtom s ++ " " ++ prettyTypeExprAtom t prettyTypeExpr (TEPattern t)  = "Pattern " ++ prettyTypeExprAtom t prettyTypeExpr (TETensor t) = "Tensor " ++ prettyTypeExprAtom t prettyTypeExpr (TEApp t args) =   prettyTypeExprAtom t ++ " " ++ unwords (map prettyTypeExprAtom args)+prettyTypeExpr (TEIO t) = "IO " ++ prettyTypeExprAtom t+prettyTypeExpr (TEVector t) = "Vector " ++ prettyTypeExprAtom t+prettyTypeExpr (TEMatrix t) = "Matrix " ++ prettyTypeExprAtom t+prettyTypeExpr (TEDiffForm t) = "DiffForm " ++ prettyTypeExprAtom t+prettyTypeExpr (TEConstrained cs t) = prettyConstraintExprs cs ++ " " ++ prettyTypeExpr t+  where+    prettyConstraintExprs [] = ""+    prettyConstraintExprs constraints = "{" ++ intercalate ", " (map prettyConstraintExpr constraints) ++ "}"+    prettyConstraintExpr _ = "..."  -- TODO: implement constraint printing+-- New CAS types+prettyTypeExpr TEFactor = "Factor"+prettyTypeExpr (TETerm t ss) = "Term " ++ prettyTypeExprAtom t ++ " " ++ prettySymbolSetExpr ss+prettyTypeExpr (TEFrac t) = "Frac " ++ prettyTypeExprAtom t+prettyTypeExpr (TEPoly t ss) = "Poly " ++ prettyTypeExprAtom t ++ " " ++ prettySymbolSetExpr ss +-- | Pretty print a SymbolSetExpr+prettySymbolSetExpr :: SymbolSetExpr -> String+prettySymbolSetExpr (SSEClosed syms) = "[" ++ intercalate ", " (map prettyTypeAtomExpr syms) ++ "]"+  where+    prettyTypeAtomExpr (TAEName s)        = s+    prettyTypeAtomExpr (TAEInt n)         = show n+    prettyTypeAtomExpr (TAEApp fn args)   = unwords (fn : map prettyAtomExprArg args)+    prettyAtomExprArg a@(TAEApp _ _) = "(" ++ prettyTypeAtomExpr a ++ ")"+    prettyAtomExprArg a              = prettyTypeAtomExpr a+prettySymbolSetExpr SSEOpen          = "[..]"+ -- | Pretty print an atomic TypeExpr prettyTypeExprAtom :: TypeExpr -> String prettyTypeExprAtom t@TEInt       = prettyTypeExpr t-prettyTypeExprAtom t@TEMathExpr  = prettyTypeExpr t+prettyTypeExprAtom t@TEMathValue  = prettyTypeExpr t prettyTypeExprAtom t@TEFloat     = prettyTypeExpr t prettyTypeExprAtom t@TEBool      = prettyTypeExpr t prettyTypeExprAtom t@TEChar      = prettyTypeExpr t@@ -153,5 +186,6 @@ prettyTypeExprAtom t@(TEVar _)   = prettyTypeExpr t prettyTypeExprAtom t@(TEList _)  = prettyTypeExpr t prettyTypeExprAtom t@(TETuple _) = prettyTypeExpr t+prettyTypeExprAtom t@TEFactor    = prettyTypeExpr t prettyTypeExprAtom t             = "(" ++ prettyTypeExpr t ++ ")" 
+ hs-src/Language/Egison/Type/RuntimeType.hs view
@@ -0,0 +1,108 @@+{- |+Module      : Language.Egison.Type.RuntimeType+Licence     : MIT++Shallow runtime-type computation for CAS values, used by the runtime-type+dispatch mechanism (see design/runtime-type-dispatch.md).++The shallow `runtimeTypeOfCAS` walks at most two levels of the value: the+outer constructor and one level of inner coefficient/numerator/denominator.+This keeps the operation O(1) at the cost of representing nested+`Frac (Poly Integer [..])` etc. as `Frac MathValue`. Instances that need+finer-grained dispatch should pattern match on the value inside their body.+-}++module Language.Egison.Type.RuntimeType+  ( runtimeTypeOfCAS+  , shallowTypeOfCAS+  ) where++import           Data.List                 (nub, sort)+import           Language.Egison.Math.CAS  (CASTerm (..), CASValue (..),+                                            SymbolExpr (..), prettyCAS)+import           Language.Egison.Type.Types (SymbolSet (..), Type (..),+                                             TypeAtom (..))++-- | Compute the shallow runtime type of a CAS value.+-- Looks at the outer constructor and one level deeper. Inner Poly/Frac are+-- abstracted to TMathValue. See design/runtime-type-dispatch.md §3.+runtimeTypeOfCAS :: CASValue -> Type+runtimeTypeOfCAS (CASInteger _) = TInt+runtimeTypeOfCAS (CASFactor _)  = TFactor+runtimeTypeOfCAS (CASPoly [])   = TInt+runtimeTypeOfCAS (CASPoly [CASTerm c []]) | isCASInteger c = TInt+runtimeTypeOfCAS (CASPoly [term@(CASTerm coef _)]) =+  let atoms = extractAtomsAsTypeAtoms [term]+  in TTerm (shallowTypeOfCAS coef) (SymbolSetClosed atoms)+runtimeTypeOfCAS (CASPoly terms) =+  let coefType = shallowJoinCoefs terms+      atoms    = extractAtomsAsTypeAtoms terms+  in TPoly coefType (SymbolSetClosed atoms)+runtimeTypeOfCAS (CASFrac n d) =+  TFrac (shallowJoinTypes (shallowTypeOfCAS n) (shallowTypeOfCAS d))++-- | Shallow type: return the outer constructor type only. Nested Poly/Frac+-- collapse to TMathValue (the design's depth cap).+shallowTypeOfCAS :: CASValue -> Type+shallowTypeOfCAS (CASInteger _) = TInt+shallowTypeOfCAS (CASFactor _)  = TFactor+shallowTypeOfCAS (CASPoly _)    = TMathValue+shallowTypeOfCAS (CASFrac _ _)  = TMathValue++-- | Predicate: is this CAS value the integer constructor?+isCASInteger :: CASValue -> Bool+isCASInteger (CASInteger _) = True+isCASInteger _              = False++-- | Compute the join of the coefficient types of a list of terms, using+-- shallow types only. Used when the polynomial has multiple terms.+shallowJoinCoefs :: [CASTerm] -> Type+shallowJoinCoefs []    = TInt+shallowJoinCoefs terms =+  foldr1 shallowJoinTypes [shallowTypeOfCAS c | CASTerm c _ <- terms]++-- | Shallow join: combine two outer types. Anything beyond Integer/Factor+-- widens to MathValue, matching the design's coarse-grained join.+shallowJoinTypes :: Type -> Type -> Type+shallowJoinTypes t1 t2+  | t1 == t2                          = t1+  | t1 == TInt && t2 == TFactor       = TFactor+  | t1 == TFactor && t2 == TInt       = TFactor+  | otherwise                         = TMathValue++-- | Collect the distinct atoms appearing in the monomials of a term list,+-- as TypeAtoms suitable for embedding in `SymbolSetClosed`.+extractAtomsAsTypeAtoms :: [CASTerm] -> [TypeAtom]+extractAtomsAsTypeAtoms terms =+  let atoms = [symbolToTypeAtom s | CASTerm _ mono <- terms, (s, _) <- mono]+  in nub (sort atoms)++-- | Convert a SymbolExpr to a TypeAtom for use in symbol sets.+-- Symbols become TANameAtom; Apply1-4 become TAApplyAtom; everything else+-- falls back to TANameAtom of its pretty form.+symbolToTypeAtom :: SymbolExpr -> TypeAtom+symbolToTypeAtom (Symbol _ name _) = TANameAtom name+symbolToTypeAtom (Apply1 fn a1) =+  TAApplyAtom (extractFnName fn) [casValueToTypeAtom a1]+symbolToTypeAtom (Apply2 fn a1 a2) =+  TAApplyAtom (extractFnName fn) [casValueToTypeAtom a1, casValueToTypeAtom a2]+symbolToTypeAtom (Apply3 fn a1 a2 a3) =+  TAApplyAtom (extractFnName fn)+    [casValueToTypeAtom a1, casValueToTypeAtom a2, casValueToTypeAtom a3]+symbolToTypeAtom (Apply4 fn a1 a2 a3 a4) =+  TAApplyAtom (extractFnName fn)+    [casValueToTypeAtom a1, casValueToTypeAtom a2, casValueToTypeAtom a3, casValueToTypeAtom a4]+symbolToTypeAtom other = TANameAtom (show other)++-- | Extract a function name from a CASValue head: typically a Symbol factor.+extractFnName :: CASValue -> String+extractFnName (CASFactor (Symbol _ name _)) = name+extractFnName v                              = prettyCAS v++-- | Convert an arbitrary CASValue argument to a TypeAtom.+-- Integers become TAIntAtom; symbols become TANameAtom; complex values+-- fall back to TANameAtom of their pretty form.+casValueToTypeAtom :: CASValue -> TypeAtom+casValueToTypeAtom (CASInteger n)              = TAIntAtom n+casValueToTypeAtom (CASFactor (Symbol _ s _))  = TANameAtom s+casValueToTypeAtom v                           = TANameAtom (prettyCAS v)
hs-src/Language/Egison/Type/Subst.hs view
@@ -26,7 +26,7 @@ import           GHC.Generics               (Generic)  import           Language.Egison.Type.Index (Index (..), IndexSpec, IndexTyVar (..))-import           Language.Egison.Type.Types (TyVar (..), Type (..), TypeScheme (..), Constraint(..))+import           Language.Egison.Type.Types (TyVar (..), Type (..), TypeScheme (..), Constraint(..), SymbolSet(..))  -- | Type substitution: a mapping from type variables to types newtype Subst = Subst { unSubst :: Map TyVar Type }@@ -49,7 +49,7 @@ -- | Apply a substitution to a type applySubst :: Subst -> Type -> Type applySubst _ TInt             = TInt-applySubst _ TMathExpr        = TMathExpr+applySubst _ TMathValue        = TMathValue applySubst _ TPolyExpr        = TPolyExpr applySubst _ TTermExpr        = TTermExpr applySubst _ TSymbolExpr      = TSymbolExpr@@ -65,12 +65,27 @@ applySubst s (TTensor t)      = TTensor (applySubst s t) applySubst s (THash k v)      = THash (applySubst s k) (applySubst s v) applySubst s (TMatcher t)     = TMatcher (applySubst s t)+applySubst s (TMatcherSlot a b) = TMatcherSlot (applySubst s a) (applySubst s b) applySubst s (TFun t1 t2)     = TFun (applySubst s t1) (applySubst s t2) applySubst s (TIO t)          = TIO (applySubst s t) applySubst s (TIORef t)       = TIORef (applySubst s t) applySubst _ TPort            = TPort applySubst _ TAny             = TAny+-- New CAS types+applySubst _ TFactor          = TFactor+applySubst s (TTerm t ss)      = TTerm (applySubst s t) (applySubstSymbolSet s ss)+applySubst s (TFrac t)         = TFrac (applySubst s t)+applySubst s (TPoly t ss)     = TPoly (applySubst s t) (applySubstSymbolSet s ss) +-- | Apply a substitution to a SymbolSet+applySubstSymbolSet :: Subst -> SymbolSet -> SymbolSet+applySubstSymbolSet _ ss@(SymbolSetClosed _) = ss+applySubstSymbolSet _ SymbolSetOpen = SymbolSetOpen+applySubstSymbolSet (Subst m) ss@(SymbolSetVar v) =+  case Map.lookup v m of+    Just (TPoly _ ss') -> ss'  -- If variable maps to a Poly type, extract its symbol set+    _                  -> ss   -- Otherwise keep as variable+ -- | Apply a substitution to a type scheme applySubstScheme :: Subst -> TypeScheme -> TypeScheme applySubstScheme (Subst m) (Forall vs cs t) =@@ -80,7 +95,7 @@  -- | Apply a substitution to a constraint applySubstConstraint :: Subst -> Constraint -> Constraint-applySubstConstraint s (Constraint cls ty) = Constraint cls (applySubst s ty)+applySubstConstraint s (Constraint cls tys) = Constraint cls (map (applySubst s) tys)  -- | Index substitution: mapping from index variables to indices newtype SubstIndex = SubstIndex { unSubstIndex :: Map IndexTyVar Index }
+ hs-src/Language/Egison/Type/Subtype.hs view
@@ -0,0 +1,281 @@+{- |+Module      : Language.Egison.Type.Subtype+Licence     : MIT++Phase beta of the extensible CAS tower (design/type-cas-tower.md D1/D5,+design/type-cas-tower-implementation.md section 3).++This module implements the dynamic subtype order: a structural skeleton+(the design document's inclusion table, complete with coefficient+propagation) extended with user-declared `declare cas-subtype A ⊂ B`+edges, plus the D1 declare-time join-semilattice check:++  * an edge already derivable from the order is redundant (warning; it is+    still stored so its endpoints participate in later checks)+  * an edge whose reverse already holds would collapse the order (error)+  * an edge that leaves some pair of nodes with two incomparable minimal+    upper bounds is ambiguous (error, with suggested completing edges)+  * an edge that places the target strictly below an existing join+    refines that join (warning; refinement monotonicity — values never+    change, static types only become more precise)++The pair enumeration runs over declared nodes (all endpoints ever+declared, including redundant edges) — a finite approximation of the+full scheme-level check, to be refined counterexample-driven+(implementation plan, section 7).++This order also backs instance resolution (Type.Instance and Core's+runtime dispatch) through 'isSubtypeWith', which additionally treats+'TAny' as the top element for arbitrary (non-CAS) instance types. The+older, partial relation that instance resolution used to carry+separately ('Type.Join.isSubtype') has been retired.+-}++module Language.Egison.Type.Subtype+  ( SubtypeEdge+  , SubtypeEnv+  , isCasType+  , symbolSetSubset+  , sameCasHead+  , skeletonSubtype+  , skeletonJoin+  , isSubtypeWith+  , joinTypesWith+  , EdgeCheck(..)+  , checkEdgeAddition+  ) where++import           Data.List                  (nub)++import           Language.Egison.Type.Types (SymbolSet (..), Type (..))++-- | A declared subtype edge (lhs ⊂ rhs), with cas-type aliases expanded.+type SubtypeEdge = (Type, Type)++-- | All declared edges, in declaration order (redundant ones included).+type SubtypeEnv = [SubtypeEdge]++-- | The types the CAS order talks about. Everything else only relates+-- to itself (reflexivity).+isCasType :: Type -> Bool+isCasType TInt         = True+isCasType TMathValue   = True+isCasType TFactor      = True+isCasType (TTerm t _)  = isCasType t+isCasType (TPoly t _)  = isCasType t+isCasType (TFrac t)    = isCasType t+isCasType _            = False++-- | Same top-level CAS constructor. Instance dispatch uses this to break+-- ties among order-EQUIVALENT most-specific candidates: with the complete+-- skeleton, types like Poly MathValue [..] and Frac MathValue embed into+-- each other (constant embedding one way, numerator embedding the other —+-- only possible at the MathValue-coefficient level, where both denote the+-- full value domain). Order-equivalent types are the same value set in+-- different canonical forms, so specificity cannot decide between them;+-- per the design principle (types select representations), dispatch+-- follows the target's own representation head.+sameCasHead :: Type -> Type -> Bool+sameCasHead (TPoly _ _) (TPoly _ _) = True+sameCasHead (TTerm _ _) (TTerm _ _) = True+sameCasHead (TFrac _)   (TFrac _)   = True+sameCasHead a b = a == b++-- | Check if one symbol set is a subset of another.+symbolSetSubset :: SymbolSet -> SymbolSet -> Bool+symbolSetSubset _ SymbolSetOpen = True  -- Everything is subset of open+symbolSetSubset SymbolSetOpen (SymbolSetClosed _) = False  -- Open not subset of closed+symbolSetSubset (SymbolSetClosed s1) (SymbolSetClosed s2) = all (`elem` s2) s1+symbolSetSubset (SymbolSetVar _) _ = True  -- Assume vars are subset (will be resolved)+symbolSetSubset _ (SymbolSetVar _) = True++--------------------------------------------------------------------------------+-- Structural skeleton+--------------------------------------------------------------------------------++-- | The structural skeleton subtype relation: the design document's+-- inclusion table (type-cas.md 型の包含関係) with its propagation rules,+-- applied recursively through coefficients:+--+--   * Integer embeds into Factor and into any Frac/Poly/Term whose+--     coefficient admits Integer+--   * Factor embeds where Integer-coefficient polys live+--   * Frac a embeds into Poly b s when Frac a embeds into the+--     coefficient b (constant embedding, e.g. Frac Integer ⊂ Poly (Frac+--     Integer) s)+--   * Term/Poly are covariant in the coefficient with atom-set inclusion+--   * Poly/Term a s ⊂ Frac b when they embed into b (numerator embedding)+--+-- Deliberately NOT included: relating nested and flat canonical forms+-- (e.g. Poly (Poly Integer [i]) [x] vs Poly Integer [i, x]). Those are+-- different canonical forms of the same value set; placing them in the+-- order is exactly what user-declared edges are for (usecase 04/08).+skeletonSubtype :: Type -> Type -> Bool+skeletonSubtype a b | a == b = True+skeletonSubtype a TMathValue = isCasType a+-- Integer+skeletonSubtype TInt TFactor     = True+skeletonSubtype TInt (TFrac t)   = t == TInt || skeletonSubtype TInt t+skeletonSubtype TInt (TPoly t _) = t == TInt || skeletonSubtype TInt t+skeletonSubtype TInt (TTerm t _) = t == TInt || skeletonSubtype TInt t+-- Factor (atomic element; lives wherever Integer coefficients do)+skeletonSubtype TFactor (TPoly t _) = t == TInt || skeletonSubtype TInt t+skeletonSubtype TFactor (TTerm t _) = t == TInt || skeletonSubtype TInt t+skeletonSubtype TFactor (TFrac t)   = skeletonSubtype TFactor t+-- Term+skeletonSubtype (TTerm t1 s1) (TTerm t2 s2) =+  skeletonSubtype t1 t2 && symbolSetSubset s1 s2+skeletonSubtype (TTerm t1 s1) (TPoly t2 s2) =+  skeletonSubtype t1 t2 && symbolSetSubset s1 s2+skeletonSubtype (TTerm t1 s1) (TFrac t2) = skeletonSubtype (TTerm t1 s1) t2+-- Poly+skeletonSubtype (TPoly t1 s1) (TPoly t2 s2) =+  skeletonSubtype t1 t2 && symbolSetSubset s1 s2+skeletonSubtype (TPoly t1 s1) (TFrac t2) = skeletonSubtype (TPoly t1 s1) t2+-- Frac+skeletonSubtype (TFrac t1) (TFrac t2)   = skeletonSubtype t1 t2+skeletonSubtype (TFrac t1) (TPoly t2 _) = skeletonSubtype (TFrac t1) t2+skeletonSubtype (TFrac t1) (TTerm t2 _) = skeletonSubtype (TFrac t1) t2+skeletonSubtype _ _ = False++-- | Skeleton join, following the design join table (type-cas.md+-- join の計算規則), including the documented tower rule+-- level 2 ⊔ level 3 = level 4:+--+--   join(Poly a s, Frac X) = Poly (join a (Frac X)) s   -- X non-Poly+--   join(Poly a s, Frac (Poly b s')) = Frac (Poly (join a b) (s ∪ s'))+--+-- (The legacy 'Type.Join.joinTypes', which returned level 5 for the first+-- case in disagreement with the design, had no callers and was removed.)+skeletonJoin :: Type -> Type -> Maybe Type+skeletonJoin a b+  | not (isCasType a) || not (isCasType b) = Nothing+  | skeletonSubtype a b = Just b+  | skeletonSubtype b a = Just a+skeletonJoin (TTerm t1 s1) (TTerm t2 s2) =+  TTerm <$> skeletonJoin t1 t2 <*> unionSymbolSets s1 s2+skeletonJoin (TTerm t1 s1) p@(TPoly _ _) = skeletonJoin (TPoly t1 s1) p+skeletonJoin p@(TPoly _ _) (TTerm t2 s2) = skeletonJoin p (TPoly t2 s2)+skeletonJoin (TTerm t1 s1) f@(TFrac _) = skeletonJoin (TPoly t1 s1) f+skeletonJoin f@(TFrac _) (TTerm t2 s2) = skeletonJoin f (TPoly t2 s2)+skeletonJoin (TPoly t1 s1) (TPoly t2 s2) =+  TPoly <$> skeletonJoin t1 t2 <*> unionSymbolSets s1 s2+skeletonJoin (TPoly t1 s1) (TFrac t2) = joinPolyFrac t1 s1 t2+skeletonJoin (TFrac t1) (TPoly t2 s2) = joinPolyFrac t2 s2 t1+skeletonJoin (TFrac t1) (TFrac t2) = TFrac <$> skeletonJoin t1 t2+skeletonJoin _ _ = Nothing++-- | join(Poly a s, Frac x): level 4 when x is a scalar domain, level 5+-- when x is itself a Poly (rational-function field).+joinPolyFrac :: Type -> SymbolSet -> Type -> Maybe Type+joinPolyFrac a s (TPoly b s') =+  TFrac <$> (TPoly <$> skeletonJoin a b <*> unionSymbolSets s s')+joinPolyFrac a s x = do+  coeff <- skeletonJoin a (TFrac x)+  return (TPoly coeff s)++-- | Union of atom sets: open absorbs, closed sets take the ordered union.+unionSymbolSets :: SymbolSet -> SymbolSet -> Maybe SymbolSet+unionSymbolSets SymbolSetOpen _ = Just SymbolSetOpen+unionSymbolSets _ SymbolSetOpen = Just SymbolSetOpen+unionSymbolSets (SymbolSetClosed s1) (SymbolSetClosed s2) =+  Just (SymbolSetClosed (nub (s1 ++ s2)))+unionSymbolSets (SymbolSetVar v1) (SymbolSetVar v2)+  | v1 == v2 = Just (SymbolSetVar v1)+  | otherwise = Just SymbolSetOpen+unionSymbolSets (SymbolSetVar _) ss = Just ss+unionSymbolSets ss (SymbolSetVar _) = Just ss++--------------------------------------------------------------------------------+-- Order with declared edges+--------------------------------------------------------------------------------++-- | Subtype in the declared order: skeleton, or a path that climbs+-- through declared edges (entering an edge from anything skeleton-below+-- its source). Worklist over edge targets; terminates because the+-- visited set only grows within the finite edge-target set.+-- TAny is the top element (instance resolution compares arbitrary+-- instance types through this relation, and TAny-typed heads accept+-- everything; TAny never occurs in declared CAS edges).+isSubtypeWith :: SubtypeEnv -> Type -> Type -> Bool+isSubtypeWith _ _ TAny = True+isSubtypeWith edges a b = go [a] [a]+  where+    go [] _ = False+    go (t:rest) visited+      | skeletonSubtype t b = True+      | otherwise =+          let nexts = [ y | (x, y) <- edges+                          , skeletonSubtype t x || t == x+                          , y `notElem` visited ]+          in go (rest ++ nexts) (visited ++ nexts)++-- | Join in the declared order: the unique minimal upper bound among the+-- skeleton join and the declared nodes. Ambiguity should be prevented by+-- the declare-time check; if it happens anyway we prefer the skeleton+-- join, and give up otherwise.+joinTypesWith :: SubtypeEnv -> Type -> Type -> Maybe Type+joinTypesWith edges a b =+  case minimals of+    [j] -> Just j+    []  -> Nothing+    js  -> case [ j | j <- js, Just j == skel ] of+             [j] -> Just j+             _   -> Nothing+  where+    skel = skeletonJoin a b+    nodes = nub (concatMap (\(x, y) -> [x, y]) edges)+    uppers = nub ( [ j | Just j <- [skel] ]+                ++ [ n | n <- nodes+                       , isSubtypeWith edges a n+                       , isSubtypeWith edges b n ] )+    minimals = [ u | u <- uppers, not (any (strictlyBelow u) uppers) ]+    strictlyBelow u v =+      v /= u && isSubtypeWith edges v u && not (isSubtypeWith edges u v)++--------------------------------------------------------------------------------+-- D1 declare-time check+--------------------------------------------------------------------------------++-- | Result of checking a new edge against the current order.+data EdgeCheck+  = EdgeRedundant+    -- ^ Already derivable; harmless (store it, warn).+  | EdgeCycle+    -- ^ The reverse relation already holds; adding the edge would+    -- collapse the two types into one order point (rejected).+  | EdgeAmbiguous [(Type, Type, Type)]+    -- ^ Pairs (x, y, oldJoin) whose minimal upper bounds would split+    -- into {oldJoin, target}; suggest declaring oldJoin ⊂ target.+  | EdgeRefines [(Type, Type, Type)]+    -- ^ Pairs (x, y, oldJoin) whose join refines from oldJoin down to+    -- the target (accepted with a warning).+  | EdgeOk+  deriving (Eq, Show)++-- | D1 join-semilattice check for adding edge (a ⊂ b).+-- Pairs are drawn from declared nodes below b in the extended order.+checkEdgeAddition :: SubtypeEnv -> SubtypeEdge -> EdgeCheck+checkEdgeAddition edges (a, b)+  | isSubtypeWith edges a b = EdgeRedundant+  | isSubtypeWith edges b a = EdgeCycle+  | not (null ambiguous)    = EdgeAmbiguous ambiguous+  | not (null refines)      = EdgeRefines refines+  | otherwise               = EdgeOk+  where+    edges' = (a, b) : edges+    nodes = nub ([a, b] ++ concatMap (\(x, y) -> [x, y]) edges)+    below = [ n | n <- nodes, n /= b, isSubtypeWith edges' n b ]+    pairs = [ (x, y) | x <- below, y <- below, x < y ]+    classified = [ (p, c) | p <- pairs, Just c <- [classify p] ]+    ambiguous = [ (x, y, j) | ((x, y), Right j) <- classified ]+    refines   = [ (x, y, j) | ((x, y), Left j)  <- classified ]+    -- Right = ambiguous (old join incomparable with b),+    -- Left  = refinement (b strictly below old join).+    classify (x, y) =+      case joinTypesWith edges x y of+        Nothing -> Nothing            -- no old join: b becomes one (fine)+        Just j+          | isSubtypeWith edges' j b -> Nothing   -- old join stays minimal+          | isSubtypeWith edges' b j -> Just (Left j)+          | otherwise                -> Just (Right j)
hs-src/Language/Egison/Type/Tensor.hs view
@@ -26,7 +26,8 @@ normalizeTensorType (TInductive name ts) = TInductive name (map normalizeTensorType ts) normalizeTensorType (THash k v) = THash (normalizeTensorType k) (normalizeTensorType v) normalizeTensorType (TMatcher t) = TMatcher (normalizeTensorType t)+normalizeTensorType (TMatcherSlot s t) = TMatcherSlot (normalizeTensorType s) (normalizeTensorType t) normalizeTensorType (TFun a r) = TFun (normalizeTensorType a) (normalizeTensorType r) normalizeTensorType (TIO t) = TIO (normalizeTensorType t) normalizeTensorType (TIORef t) = TIORef (normalizeTensorType t)-normalizeTensorType t = t  -- TInt, TMathExpr, TPolyExpr, TTermExpr, TSymbolExpr, TIndexExpr, TFloat, TBool, TChar, TString, TVar, TAny+normalizeTensorType t = t  -- TInt, TMathValue, TPolyExpr, TTermExpr, TSymbolExpr, TIndexExpr, TFloat, TBool, TChar, TString, TVar, TAny
hs-src/Language/Egison/Type/TensorMapInsertion.hs view
@@ -2,21 +2,26 @@ Module      : Language.Egison.Type.TensorMapInsertion Licence     : MIT -This module implements automatic tensorMap insertion for Phase 8 of the Egison compiler.+This module implements automatic tensorMap insertion for Phase 7 of the Egison compiler. This is the first step of TypedDesugar, before type class expansion. When a function expects a scalar type (e.g., Integer) but receives a Tensor type, this module automatically inserts tensorMap to apply the function element-wise. -Two insertion modes:+Four implementation forms: 1. Direct application: When argument is Tensor and parameter expects scalar,    wrap the application with tensorMap.-2. Higher-order functions (simplified approach): When a binary function with-   constrained/scalar parameter types is passed as an argument, always wrap-   it with tensorMap2. This handles cases like `foldl1 (+) xs` where elements-   of xs might be Tensors at runtime.+2. Type-directed higher-order lifting: when a scalar function is passed to a+   callback position that expects tensor arguments, eta-expand it and map those+   tensor arguments back to scalar arguments.+3. Feedback-aware higher-order lifting: when a lifted callback result feeds+   back into a callback parameter, e.g. the accumulator in foldl, lift that+   parameter too.+4. Derived binary-map compatibility: when a binary scalar function is passed as+   a value, e.g. `foldl1 (+) xs`, use tensorMap2 so reduction accumulators can+   become tensors without rebuilding the nested maps by hand.  According to tensor-map-insertion-simple.md:-- When a binary function with scalar parameter types is passed as an argument, always wrap it with tensorMap2+- tensorMap2 is semantically equivalent to nested tensorMap - tensorMap/tensorMap2 act as identity for scalar values, so wrapping is safe regardless of whether the actual argument is a tensor or scalar  Example:@@ -26,6 +31,15 @@    def sum {Num a} (xs: [a]) : a := foldl1 (+) xs   --=>  def sum {Num a} (xs: [a]) : a := foldl1 (tensorMap2 (+)) xs++  map inc [t1]+  --=>  map (\x -> tensorMap inc x) [t1]++  map2 (*) [t1] [10]+  --=>  map2 (\x y -> tensorMap (\xe -> (*) xe y) x) [t1] [10]++  foldl (+) 0 [t1]+  --=>  foldl (\acc x -> tensorMap2 (+) acc x) 0 [t1] -}  module Language.Egison.Type.TensorMapInsertion@@ -38,7 +52,7 @@ import           Language.Egison.IExpr      (TIExpr(..), TIExprNode(..),                                              Var(..), tiExprType, tiScheme, tiExprNode) import           Language.Egison.Type.Env   (ClassEnv)-import           Language.Egison.Type.Tensor ()+import           Language.Egison.Type.Tensor (normalizeTensorType) import           Language.Egison.Type.Types (Type(..), TypeScheme(..), Constraint(..), TyVar(..)) import           Language.Egison.Type.Unify as Unify (unifyStrictWithConstraints) @@ -96,7 +110,7 @@ applyOneArgType t = t  -- No more arguments  ----------------------------------------------------------------------------------- * Simplified Approach: Always wrap binary functions with tensorMap2+-- * Higher-order tensor lifting --------------------------------------------------------------------------------  -- | Check if a type is a scalar type (not a Tensor type)@@ -120,65 +134,203 @@        Right _ -> False  -- Can unify with Tensor a → not scalar        Left _  -> True   -- Cannot unify with Tensor a → is scalar --- | Check if a binary function should be wrapped with tensorMap2--- A function should be wrapped if:--- 1. It's a binary function (a -> b -> c)--- 2. Both parameter types are scalar types (not Tensor types)------ For example:--- - (+) : {Num a} a -> a -> a  -- Both params are scalar → wrap with tensorMap2--- - (.) : {Num a} Tensor a -> Tensor a -> Tensor a  -- Both params are Tensor → do NOT wrap-shouldWrapWithTensorMap2 :: ClassEnv -> [Constraint] -> Type -> Bool-shouldWrapWithTensorMap2 classEnv constraints ty = case ty of-  TFun param1 (TFun param2 _result) ->-      isPotentialScalarType classEnv constraints param1 &&-      isPotentialScalarType classEnv constraints param2+-- | Exclude effectful/resource-like types from compatibility lifting. These+-- types are not tensors, but wrapping callbacks such as `io : IO a -> a` would+-- change control-flow behavior in ordinary higher-order applications.+containsNonLiftableType :: Type -> Bool+containsNonLiftableType ty = case ty of+  TIO _ -> True+  TIORef _ -> True+  TPort -> True+  TFun _ _ -> True+  TTuple ts -> any containsNonLiftableType ts+  TCollection t -> containsNonLiftableType t+  TInductive _ ts -> any containsNonLiftableType ts+  TTensor t -> containsNonLiftableType t+  THash k v -> containsNonLiftableType k || containsNonLiftableType v+  TMatcher t -> containsNonLiftableType t+  TMatcherSlot p t -> containsNonLiftableType p || containsNonLiftableType t+  TTerm t _ -> containsNonLiftableType t+  TFrac t -> containsNonLiftableType t+  TPoly t _ -> containsNonLiftableType t   _ -> False --- | Wrap a binary function expression with tensorMap2--- f : a -> b -> c  becomes  \x y -> tensorMap2 f x y--- The lambda receives TENSOR arguments and returns a TENSOR result-wrapWithTensorMap2 :: [Constraint] -> TIExpr -> TIExpr-wrapWithTensorMap2 _constraints funcExpr =-  let funcType = tiExprType funcExpr-  in case funcType of-    TFun param1 (TFun param2 result) ->-      let -- Create fresh variable names-          var1Name = "tmap2_arg1"-          var2Name = "tmap2_arg2"-          var1 = Var var1Name []-          var2 = Var var2Name []+isTensorLiftableScalarType :: ClassEnv -> [Constraint] -> Type -> Bool+isTensorLiftableScalarType classEnv constraints ty =+  isPotentialScalarType classEnv constraints ty &&+  not (containsNonLiftableType ty) -          -- Variables have TENSOR types (they receive tensor arguments)-          var1Scheme = Forall [] [] (TTensor param1)-          var2Scheme = Forall [] [] (TTensor param2)-          var1TI = TIExpr var1Scheme (TIVarExpr var1Name)-          var2TI = TIExpr var2Scheme (TIVarExpr var2Name)+-- | Split a curried function type into its argument types and result type.+collectFunctionType :: Type -> ([Type], Type)+collectFunctionType (TFun param result) =+  let (params, finalResult) = collectFunctionType result+  in (param : params, finalResult)+collectFunctionType ty = ([], ty) -          -- Result is also a TENSOR-          resultScheme = Forall [] [] (TTensor result)+-- | Build a curried function type from argument types and a result type.+buildFunctionType :: [Type] -> Type -> Type+buildFunctionType params result = foldr TFun result params -          -- Build: tensorMap2 funcExpr var1 var2-          innerNode = TITensorMap2Expr funcExpr var1TI var2TI-          innerExpr = TIExpr resultScheme innerNode+-- | Apply N arguments at the type level.+applyNArgType :: Type -> Int -> Type+applyNArgType ty 0 = ty+applyNArgType (TFun _ result) n+  | n > 0 = applyNArgType result (n - 1)+applyNArgType ty _ = ty -          -- Build lambda: \var1 var2 -> tensorMap2 funcExpr var1 var2-          -- Lambda type: Tensor a -> Tensor b -> Tensor c-          -- No constraints needed - this is just a wrapper-          lambdaType = TFun (TTensor param1) (TFun (TTensor param2) (TTensor result))-          lambdaScheme = Forall [] [] lambdaType-          lambdaNode = TILambdaExpr Nothing [var1, var2] innerExpr+-- | A parameter in a generated higher-order callback wrapper.+data CallbackParamPlan = CallbackParamPlan+  { callbackParamIndex      :: Int+  , callbackParamActualType :: Type+  , callbackParamOuterType  :: Type+  , callbackParamOuterVar   :: Var+  , callbackParamOuterExpr  :: TIExpr+  , callbackParamNeedsLift  :: Bool+  } -      in TIExpr lambdaScheme lambdaNode-    _ -> funcExpr  -- Not a binary function, return unchanged+mkVarTIExpr :: String -> Type -> TIExpr+mkVarTIExpr name ty = TIExpr (Forall [] [] ty) (TIVarExpr name) --- | Check if an expression is already wrapped with tensorMap2-isAlreadyWrappedWithTensorMap2 :: TIExprNode -> Bool-isAlreadyWrappedWithTensorMap2 (TILambdaExpr _ [_, _] body) =+mkCallbackParamPlan :: Int -> Type -> Bool -> CallbackParamPlan+mkCallbackParamPlan index actualParam needsLift =+  let outerType = if needsLift then TTensor actualParam else actualParam+      outerName = "tmap_arg" ++ show (index + 1)+      outerVar = Var outerName []+      outerExpr = mkVarTIExpr outerName outerType+  in CallbackParamPlan index actualParam outerType outerVar outerExpr needsLift++sameNormalizedType :: Type -> Type -> Bool+sameNormalizedType ty1 ty2 =+  normalizeTensorType ty1 == normalizeTensorType ty2++-- | A callback parameter is a direct lift seed when the higher-order function+-- expects a tensor argument there but the supplied function consumes a scalar.+isDirectLiftSeed :: ClassEnv -> [Constraint] -> Type -> Type -> Bool+isDirectLiftSeed classEnv constraints expectedParam actualParam =+  case expectedParam of+    TTensor _ -> isTensorLiftableScalarType classEnv constraints actualParam+    _ -> False++-- | Detect whether a callback's result is fed back by the surrounding+-- higher-order function as a naked value. This distinguishes reductions like+-- foldl/foldr/scanl from maps, whose callback result only appears under a list.+callbackResultFeedsBack :: Type -> Int -> Type -> Bool+callbackResultFeedsBack outerFuncType callbackArgIndex expectedCallbackType =+  let (outerParams, outerResult) = collectFunctionType outerFuncType+      (_, callbackResult) = collectFunctionType expectedCallbackType+      laterOuterParams = drop (callbackArgIndex + 1) outerParams+  in any (sameNormalizedType callbackResult) (outerResult : laterOuterParams)++-- | Compute the callback parameters that should be tensor-lifted.+--+-- First seed the positions that the expected callback type already marks as+-- Tensor. If such a seed makes the scalar callback result tensor-valued, and+-- the surrounding higher-order function feeds that result back, propagate the+-- lift to callback parameters whose expected type is the callback result type.+callbackLiftMask ::+    ClassEnv+    -> [Constraint]+    -> Bool+    -> [Type]+    -> Type+    -> [Type]+    -> Type+    -> [Bool]+callbackLiftMask classEnv constraints resultFeedsBack expectedParams expectedResult actualParams actualResult =+  let initialMask = zipWith (isDirectLiftSeed classEnv constraints) expectedParams actualParams+      resultCanBecomeTensor mask =+        any id mask && isTensorLiftableScalarType classEnv constraints actualResult+      step mask =+        zipWith3+          (\already expectedParam actualParam ->+             already ||+             ( resultFeedsBack+             && resultCanBecomeTensor mask+             && sameNormalizedType expectedParam expectedResult+             && isTensorLiftableScalarType classEnv constraints actualParam))+          mask+          expectedParams+          actualParams+      go mask =+        let mask' = step mask+        in if mask' == mask then mask else go mask'+  in go initialMask++-- | Build a wrapper plan for a scalar callback passed to a higher-order+-- argument position. The plan is absent when the expected callback type does+-- not force any tensor lifting.+buildCallbackLiftPlan ::+    ClassEnv+    -> [Constraint]+    -> Type+    -> Int+    -> Type+    -> TIExpr+    -> Maybe ([CallbackParamPlan], [CallbackParamPlan], Type)+buildCallbackLiftPlan classEnv constraints outerFuncType callbackArgIndex expectedCallbackType funcExpr =+  let actualFuncType = tiExprType funcExpr+      (expectedParams, expectedResult) = collectFunctionType expectedCallbackType+      (actualParams, actualResult) = collectFunctionType actualFuncType+      arity = length expectedParams+  in if arity == 0 || length actualParams < arity+       then Nothing+       else+         let actualParamsForArity = take arity actualParams+             resultFeedsBack =+               callbackResultFeedsBack outerFuncType callbackArgIndex expectedCallbackType+             liftMask =+               callbackLiftMask+                 classEnv+                 constraints+                 resultFeedsBack+                 expectedParams+                 expectedResult+                 actualParamsForArity+                 actualResult+             callbackParams = zipWith3 mkCallbackParamPlan [0..] actualParamsForArity liftMask+             liftedParams = filter callbackParamNeedsLift callbackParams+             resultType = applyNArgType actualFuncType arity+         in if null liftedParams+              then Nothing+              else Just (callbackParams, liftedParams, resultType)++-- | Check if a binary scalar function can use the compatibility tensorMap2+-- wrapper. This keeps polymorphic scalar callbacks such as `foldl (*)` working+-- even when the callback type itself does not mention Tensor yet.+shouldUseTensorMap2Fallback :: ClassEnv -> [Constraint] -> Type -> Bool+shouldUseTensorMap2Fallback classEnv constraints ty =+  case collectFunctionType ty of+    ([param1, param2], _result) ->+      isTensorLiftableScalarType classEnv constraints param1 &&+      isTensorLiftableScalarType classEnv constraints param2+    _ -> False++-- | Compatibility wrapper for binary scalar callbacks.+wrapWithTensorMap2Fallback :: TIExpr -> TIExpr+wrapWithTensorMap2Fallback funcExpr =+  case tiExprType funcExpr of+    TFun param1 (TFun param2 result) ->+      let varName1 = "tmap2_arg1"+          varName2 = "tmap2_arg2"+          var1 = Var varName1 []+          var2 = Var varName2 []+          var1TI = mkVarTIExpr varName1 (TTensor param1)+          var2TI = mkVarTIExpr varName2 (TTensor param2)+          innerType = normalizeTensorType (TTensor result)+          innerExpr = TIExpr (Forall [] [] innerType) (TITensorMap2Expr funcExpr var1TI var2TI)+          lambdaType = TFun (TTensor param1) (TFun (TTensor param2) innerType)+          lambdaScheme = Forall [] [] lambdaType+      in TIExpr lambdaScheme (TILambdaExpr Nothing [var1, var2] innerExpr)+    _ -> funcExpr++-- | Check if a lambda already has a tensorMap/tensorMap2 body.+isAlreadyWrappedWithTensorMap :: TIExprNode -> Bool+isAlreadyWrappedWithTensorMap (TILambdaExpr _ _ body) =   case tiExprNode body of+    TITensorMapExpr _ _ -> True     TITensorMap2Expr _ _ _ -> True     _ -> False-isAlreadyWrappedWithTensorMap2 _ = False+isAlreadyWrappedWithTensorMap _ = False  -------------------------------------------------------------------------------- -- * TensorMap Insertion Implementation@@ -192,53 +344,106 @@   let scheme = tiScheme tiExpr   insertTensorMapsInExpr classEnv scheme tiExpr --- | Wrap a binary function with tensorMap2 if it should be wrapped--- This implements the simplified approach from tensor-map-insertion-simple.md-wrapBinaryFunctionIfNeeded :: ClassEnv -> [Constraint] -> TIExpr -> TIExpr-wrapBinaryFunctionIfNeeded classEnv constraints tiExpr =-  let exprType = tiExprType tiExpr-      node = tiExprNode tiExpr-  in -- Don't wrap if already wrapped with tensorMap2-     if isAlreadyWrappedWithTensorMap2 node-       then tiExpr-       else case node of-         -- For binary lambda expressions like \x y -> f x y, wrap the body with tensorMap2-         -- This handles eta-expanded type class methods like \etaVar1 etaVar2 -> dict_("plus") etaVar1 etaVar2-         TILambdaExpr mVar [var1, var2] body-           | shouldWrapWithTensorMap2 classEnv constraints exprType ->-               wrapLambdaBodyWithTensorMap2 constraints mVar var1 var2 body tiExpr-         -- Don't wrap other lambda expressions-         TILambdaExpr {} -> tiExpr-         -- Don't wrap function applications (they're already being applied)-         TIApplyExpr {} -> tiExpr-         -- Wrap variable references and other expressions that represent functions-         _ | shouldWrapWithTensorMap2 classEnv constraints exprType ->-               wrapWithTensorMap2 constraints tiExpr-           | otherwise -> tiExpr+-- | Wrap a higher-order scalar function according to the callback type expected+-- by the call site. If the expected callback receives a tensor where the+-- supplied function receives a scalar, generate an eta-expanded wrapper that+-- maps the scalar function over that tensor argument.+wrapWithTypeDirectedTensorLift :: ClassEnv -> [Constraint] -> Type -> Int -> Type -> TIExpr -> Maybe TIExpr+wrapWithTypeDirectedTensorLift classEnv constraints outerFuncType callbackArgIndex expectedCallbackType funcExpr =+  case buildCallbackLiftPlan classEnv constraints outerFuncType callbackArgIndex expectedCallbackType funcExpr of+    Nothing -> Nothing+    Just (callbackParams, liftedParams, resultType) ->+      let body = buildTypeDirectedTensorLiftBody funcExpr resultType callbackParams liftedParams []+          lambdaType = buildFunctionType (map callbackParamOuterType callbackParams) (tiExprType body)+          lambdaScheme = Forall [] [] lambdaType+          lambdaNode = TILambdaExpr Nothing (map callbackParamOuterVar callbackParams) body+      in Just $ TIExpr lambdaScheme lambdaNode --- | Wrap the body of a binary lambda with tensorMap2--- Transform: \x y -> f x y  to  \x y -> tensorMap2 f x y-wrapLambdaBodyWithTensorMap2 :: [Constraint] -> Maybe Var -> Var -> Var -> TIExpr -> TIExpr -> TIExpr-wrapLambdaBodyWithTensorMap2 constraints mVar var1 var2 body originalExpr =-  case tiExprNode body of-    -- Body is a function application: \x y -> f x y-    TIApplyExpr func args-      | length args == 2 ->-          let arg1 = args !! 0-              arg2 = args !! 1-              -- Create tensorMap2 f arg1 arg2-              resultType = tiExprType body-              resultScheme = Forall [] [] resultType-              newBody = TIExpr resultScheme (TITensorMap2Expr func arg1 arg2)-              -- Rebuild the lambda with the new body-              (Forall tvs cs lambdaType) = tiScheme originalExpr-              newLambdaScheme = Forall tvs (constraints ++ cs) lambdaType-          in TIExpr newLambdaScheme (TILambdaExpr mVar [var1, var2] newBody)-    -- Body is already tensorMap2-    TITensorMap2Expr {} -> originalExpr-    -- Other cases: just wrap the whole thing-    _ -> wrapWithTensorMap2 constraints originalExpr+-- | Build the body of a generated callback wrapper.+buildTypeDirectedTensorLiftBody ::+    TIExpr+    -> Type+    -> [CallbackParamPlan]+    -> [CallbackParamPlan]+    -> [(Int, TIExpr)]+    -> TIExpr+buildTypeDirectedTensorLiftBody funcExpr resultType callbackParams [] scalarArgs =+  let argFor param =+        case lookup (callbackParamIndex param) scalarArgs of+          Just scalarArg -> scalarArg+          Nothing -> callbackParamOuterExpr param+      args = map argFor callbackParams+  in TIExpr (Forall [] [] resultType) (TIApplyExpr funcExpr args)+buildTypeDirectedTensorLiftBody funcExpr resultType callbackParams [param] scalarArgs =+  let index = callbackParamIndex param+      scalarName = "tmap_elem" ++ show (index + 1)+      scalarVar = Var scalarName []+      scalarExpr = mkVarTIExpr scalarName (callbackParamActualType param)+      inner = buildTypeDirectedTensorLiftBody+                funcExpr+                resultType+                callbackParams+                []+                ((index, scalarExpr) : scalarArgs)+      lambdaType = TFun (callbackParamActualType param) (tiExprType inner)+      lambdaExpr = TIExpr (Forall [] [] lambdaType) (TILambdaExpr Nothing [scalarVar] inner)+      mappedType = normalizeTensorType (TTensor (tiExprType inner))+  in TIExpr (Forall [] [] mappedType) (TITensorMapExpr lambdaExpr (callbackParamOuterExpr param))+buildTypeDirectedTensorLiftBody funcExpr resultType callbackParams (param1:param2:restParams) scalarArgs =+  let index1 = callbackParamIndex param1+      index2 = callbackParamIndex param2+      scalarName1 = "tmap_elem" ++ show (index1 + 1)+      scalarName2 = "tmap_elem" ++ show (index2 + 1)+      scalarVar1 = Var scalarName1 []+      scalarVar2 = Var scalarName2 []+      scalarExpr1 = mkVarTIExpr scalarName1 (callbackParamActualType param1)+      scalarExpr2 = mkVarTIExpr scalarName2 (callbackParamActualType param2)+      inner = buildTypeDirectedTensorLiftBody+                funcExpr+                resultType+                callbackParams+                restParams+                ((index2, scalarExpr2) : (index1, scalarExpr1) : scalarArgs)+      lambdaType =+        TFun (callbackParamActualType param1)+             (TFun (callbackParamActualType param2) (tiExprType inner))+      lambdaExpr =+        TIExpr (Forall [] [] lambdaType) (TILambdaExpr Nothing [scalarVar1, scalarVar2] inner)+      mappedType = normalizeTensorType (TTensor (tiExprType inner))+  in TIExpr+       (Forall [] [] mappedType)+       (TITensorMap2Expr lambdaExpr (callbackParamOuterExpr param1) (callbackParamOuterExpr param2)) +-- | Wrap a higher-order function argument with tensorMap/tensorMap2 if needed.+-- Type-directed callback lifting is tried first; the binary fallback preserves+-- older reduction behavior when no tensor seed is visible in the expected type.+tensorMap2FallbackIfNeeded :: ClassEnv -> [Constraint] -> TIExpr -> Maybe TIExpr+tensorMap2FallbackIfNeeded classEnv constraints tiExpr =+  case tiExprNode tiExpr of+    TIApplyExpr {} -> Nothing+    _ | shouldUseTensorMap2Fallback classEnv constraints (tiExprType tiExpr) ->+          Just (wrapWithTensorMap2Fallback tiExpr)+      | otherwise -> Nothing++wrapFunctionArgumentIfNeeded :: ClassEnv -> [Constraint] -> Type -> Int -> Maybe Type -> TIExpr -> TIExpr+wrapFunctionArgumentIfNeeded classEnv constraints outerFuncType argIndex expectedArgType tiExpr =+  let node = tiExprNode tiExpr+      mTypeDirected =+        case expectedArgType of+          Just expectedType ->+            wrapWithTypeDirectedTensorLift classEnv constraints outerFuncType argIndex expectedType tiExpr+          Nothing -> Nothing+      mBinaryFallback = tensorMap2FallbackIfNeeded classEnv constraints tiExpr+  in if isAlreadyWrappedWithTensorMap node+       then tiExpr+       else+         case mTypeDirected of+           Just wrappedExpr -> wrappedExpr+           Nothing ->+             case mBinaryFallback of+               Just binaryFallback -> binaryFallback+               Nothing -> tiExpr+ -- | Insert tensorMap in a TIExpr with type scheme information insertTensorMapsInExpr :: ClassEnv -> TypeScheme -> TIExpr -> EvalM TIExpr insertTensorMapsInExpr classEnv scheme tiExpr = do@@ -270,23 +475,25 @@          -- Apply simplified approach: wrap binary function arguments with tensorMap2         -- This handles cases like `foldl (+) 0 xs` where (+) needs to be wrapped because (+) is a binary function that takes two scalar arguments+        -- and `map f xs` where f is a unary scalar function that may receive tensor elements.         -- But `foldl1 (.) [t1, t2]` should not be wrapped with tensorMap2 because (.) is a binary function that takes two tensor arguments         -- IMPORTANT: Include each argument's own constraints when deciding if it needs wrapping         let (Forall _ funcConstraints _) = tiScheme func'             baseConstraints = cs ++ funcConstraints             -- For each argument, merge base constraints with the argument's own constraints-            wrapArg arg =+            funcType = tiExprType func'+            wrapArg (index, arg) =               let (Forall _ argConstraints _) = tiScheme arg                   argAllConstraints = nub (baseConstraints ++ argConstraints)-              in wrapBinaryFunctionIfNeeded env argAllConstraints arg-            args'' = map wrapArg args'+                  expectedArgType = getParamType funcType index+              in wrapFunctionArgumentIfNeeded env argAllConstraints funcType index expectedArgType arg+            args'' = map wrapArg (zip [0..] args')          -- Use the INFERRED function type (after type inference)         -- This ensures we use concrete types like Integer instead of type variables like a         -- For example, (+) has inferred type {Num Integer} Integer -> Integer -> Integer         -- instead of the polymorphic type {Num a} a -> a -> a-        let funcType = tiExprType func'-            argTypes = map tiExprType args''+        let argTypes = map tiExprType args''          -- Normal processing: check if tensorMap is needed based on parameter types         result <- wrapWithTensorMapIfNeeded env baseConstraints func' funcType args'' argTypes@@ -483,26 +690,30 @@                 isNonTensorType param1 && isNonTensorType param2               _ -> False -        if isScalarFunction && length args' == 2-          then do+        case (isScalarFunction, args') of+          (True, [arg1, arg2]) -> do             -- Insert tensorMap2Wedge for binary scalar functions-            let [arg1, arg2] = args'-                -- Preserve the function's original scheme with its constraints+            let -- Preserve the function's original scheme with its constraints                 (Forall tvs funcConstraints _) = tiScheme func'                 -- Unlift the function type to get the scalar version                 unliftedFuncType = unliftFunctionType funcType                 unliftedFunc = TIExpr (Forall tvs funcConstraints unliftedFuncType) (tiExprNode func')-                -- Get the result type after applying to tensor arguments-                resultType = case funcType of-                  TFun _ (TFun _ res) -> TTensor res  -- Lifting scalar result to Tensor-                  _ -> funcType  -- Fallback-                tensorMap2WedgeScheme = Forall [] cs resultType             return $ TITensorMap2WedgeExpr unliftedFunc arg1 arg2-          else+          _ ->             -- Keep WedgeApply for tensor functions or non-binary functions             return $ TIWedgeApplyExpr func' args'              TIFunctionExpr names -> return $ TIFunctionExpr names++      -- Runtime dispatch: traverse arguments, leave class/method/candidates intact.+      TIRuntimeDispatch className methodName candidates args -> do+        args' <- mapM (insertTensorMapsWithConstraints env cs) args+        return $ TIRuntimeDispatch className methodName candidates args'++      -- Reshape: traverse the inner expression; type annotation is metadata.+      TIReshape ty inner -> do+        inner' <- insertTensorMapsWithConstraints env cs inner+        return $ TIReshape ty inner'  -- | Helper to insert tensorMaps in a TIExpr with constraints -- IMPORTANT: Merges context constraints with expression's own constraints
hs-src/Language/Egison/Type/TypeClassExpand.hs view
@@ -6,1370 +6,1728 @@ It transforms TIExpr to TIExpr, replacing type class method calls with dictionary-based dispatch. -Pipeline: Phase 8 (TypedDesugar) - TypeClassExpand (first step)-This is executed before TensorMapInsertion to resolve type class methods-to concrete functions first.--For example, if we have:-  class Eq a where (==) : a -> a -> Bool-  instance Eq Integer where (==) x y := x = y--Then a call like:-  autoEq 1 2  (with type constraint: Eq Integer)-becomes:-  eqIntegerEq 1 2  (dictionary-based dispatch)--This eliminates the need for runtime dispatch functions like resolveEq.--}--module Language.Egison.Type.TypeClassExpand-  ( expandTypeClassMethodsT-  , expandTypeClassMethodsInPattern-  , addDictionaryParametersT-  , applyConcreteConstraintDictionaries-  , applyConcreteConstraintDictionariesInPattern-  ) where--import           Data.Char                  (toLower)-import           Data.List                  (find)-import           Data.Maybe                 (mapMaybe)-import           Data.Text                  (pack)-import           Control.Monad              (mplus)-import qualified Data.Set                   as Set--import           Language.Egison.AST        (ConstantExpr(..))-import           Language.Egison.Data       (EvalM)-import           Language.Egison.EvalState  (MonadEval(..))-import           Language.Egison.IExpr      (TIExpr(..), TIExprNode(..), IExpr(..), stringToVar,-                                             Index(..), tiExprType, tiScheme, tiExprNode,-                                             TIPattern(..), TIPatternNode(..), TILoopRange(..))-import           Language.Egison.Type.Env  (ClassEnv(..), ClassInfo(..), InstanceInfo(..),-                                             lookupInstances, lookupClass, lookupEnv)-import qualified Language.Egison.Type.Types as Types-import           Language.Egison.Type.Types (Type(..), TyVar(..), TypeScheme(..), Constraint(..), typeToName, typeConstructorName,-                                            sanitizeMethodName, freeTyVars)-import           Language.Egison.Type.Instance (findMatchingInstanceForType)---- ============================================================================--- Helper Functions (shared across the module)--- ============================================================================---- | Extract type variable substitutions from instance type and actual type--- Example: [a] -> [[Integer]] gives [(a, [Integer])]-extractTypeSubstitutions :: Type -> Type -> [(TyVar, Type)]-extractTypeSubstitutions instTy actualTy = go instTy actualTy-  where-    go (TVar v) actual = [(v, actual)]-    go (TCollection instElem) (TCollection actualElem) = go instElem actualElem-    go (TTuple instTypes) (TTuple actualTypes)-      | length instTypes == length actualTypes =-          concatMap (\(i, a) -> go i a) (zip instTypes actualTypes)-    go (TInductive _ instArgs) (TInductive _ actualArgs)-      | length instArgs == length actualArgs =-          concatMap (\(i, a) -> go i a) (zip instArgs actualArgs)-    go (TTensor instElem) (TTensor actualElem) = go instElem actualElem-    go (TFun instArg instRet) (TFun actualArg actualRet) =-      go instArg actualArg ++ go instRet actualRet-    go (THash instK instV) (THash actualK actualV) =-      go instK actualK ++ go instV actualV-    go (TMatcher instT) (TMatcher actualT) = go instT actualT-    go (TIO instT) (TIO actualT) = go instT actualT-    go (TIORef instT) (TIORef actualT) = go instT actualT-    go TPort TPort = []-    go _ _ = []---- | Apply type substitutions to a constraint-applySubstsToConstraint :: [(TyVar, Type)] -> Constraint -> Constraint-applySubstsToConstraint substs (Constraint cName cType) =-  Constraint cName (applySubstsToType substs cType)---- | Apply type substitutions to a type-applySubstsToType :: [(TyVar, Type)] -> Type -> Type-applySubstsToType substs = go-  where-    go t@(TVar v) = case lookup v substs of-                      Just newType -> newType-                      Nothing -> t-    go TInt = TInt-    go TFloat = TFloat-    go TBool = TBool-    go TChar = TChar-    go TString = TString-    go (TCollection t) = TCollection (go t)-    go (TTuple ts) = TTuple (map go ts)-    go (TInductive name ts) = TInductive name (map go ts)-    go (TTensor t) = TTensor (go t)-    go (THash k v) = THash (go k) (go v)-    go (TMatcher t) = TMatcher (go t)-    go (TFun t1 t2) = TFun (go t1) (go t2)-    go (TIO t) = TIO (go t)-    go (TIORef t) = TIORef (go t)-    go TPort = TPort-    go TAny = TAny---- | Get the arity of a function type (number of parameters)-getMethodArity :: Type -> Int-getMethodArity (TFun _ t2) = 1 + getMethodArity t2-getMethodArity _ = 0---- | Get parameter types from a function type-getParamTypes :: Type -> [Type]-getParamTypes (TFun t1 t2) = t1 : getParamTypes t2-getParamTypes _ = []---- | Apply N parameters to a function type and get the result type--- applyParamsToType (a -> b -> c) 2 = c--- applyParamsToType (a -> b -> c) 1 = b -> c-applyParamsToType :: Type -> Int -> Type-applyParamsToType (TFun _ t2) n-  | n > 0 = applyParamsToType t2 (n - 1)-applyParamsToType t _ = t  -- n == 0 or no more function types---- | Lowercase first character of a string-lowerFirst :: String -> String-lowerFirst [] = []-lowerFirst (c:cs) = toLower c : cs---- | Find a constraint that provides the given method-findConstraintForMethod :: ClassEnv -> String -> [Constraint] -> Maybe Constraint-findConstraintForMethod env methodName cs =-  find (\(Constraint className _) ->-    case lookupClass className env of-      Just classInfo -> methodName `elem` map fst (classMethods classInfo)-      Nothing -> False-  ) cs---- ============================================================================--- Main Type Class Expansion--- ============================================================================---- | Expand type class method calls in a typed expression (TIExpr)--- This function recursively processes TIExpr and replaces type class method calls--- with dictionary-based dispatch.-expandTypeClassMethodsT :: TIExpr -> EvalM TIExpr-expandTypeClassMethodsT tiExpr = do-  classEnv <- getClassEnv-  let scheme = tiScheme tiExpr-  -- Recursively process the TIExprNode with constraint information-  expandedNode <- expandTIExprNodeWithConstraints classEnv scheme (tiExprNode tiExpr)-  return $ TIExpr scheme expandedNode-  where-    -- Expand TIExprNode with constraint information from TypeScheme-    -- Note: Constraints from parent are not propagated - each node uses its own constraints-    expandTIExprNodeWithConstraints :: ClassEnv -> TypeScheme -> TIExprNode -> EvalM TIExprNode-    expandTIExprNodeWithConstraints classEnv' (Forall _vars _constraints _ty) node =-      expandTIExprNode classEnv' node--    -- Expand TIExprNode without parent constraints-    -- Each child expression uses only its own constraints from type inference-    expandTIExprNode :: ClassEnv -> TIExprNode -> EvalM TIExprNode-    expandTIExprNode classEnv' node = case node of-      -- Constants and variables: no expansion needed at node level-      -- (TIVarExpr expansion is handled at TIExpr level in expandTIExprWithConstraints)-      TIConstantExpr c -> return $ TIConstantExpr c-      TIVarExpr name -> return $ TIVarExpr name-      -      -- Lambda expressions: process body with its own constraints only-      TILambdaExpr mVar params body -> do-        -- Use only the body's own constraints (no parent constraints)-        -- Type inference has already assigned correct constraints to each expression-        body' <- expandTIExprWithConstraints classEnv' body-        return $ TILambdaExpr mVar params body'-      -      -- Application: check if it's a method call or constrained function call-      TIApplyExpr func args -> do-        -- First, expand the arguments (each uses its own constraints)-        args' <- mapM (expandTIExprWithConstraints classEnv') args--        case tiExprNode func of-          TIVarExpr methodName -> do-            -- Try to resolve if func is a method call using func's own constraints-            let (Forall _ funcConstraints _) = tiScheme func-            resolved <- tryResolveMethodCall classEnv' funcConstraints methodName args'-            case resolved of-              Just result -> return result-              Nothing -> do-                -- Not a method call - process recursively-                -- Note: Dictionary application for constrained functions-                -- is handled in TIVarExpr case of expandTIExprWithConstraints-                func' <- expandTIExprWithConstraints classEnv' func-                return $ TIApplyExpr func' args'-          _ -> do-            -- Not a simple variable: process recursively-            func' <- expandTIExprWithConstraints classEnv' func-            return $ TIApplyExpr func' args'-      -      -- Collections-      TITupleExpr exprs -> do-        exprs' <- mapM (expandTIExprWithConstraints classEnv') exprs-        return $ TITupleExpr exprs'--      TICollectionExpr exprs -> do-        exprs' <- mapM (expandTIExprWithConstraints classEnv') exprs-        return $ TICollectionExpr exprs'--      -- Control flow-      TIIfExpr cond thenExpr elseExpr -> do-        cond' <- expandTIExprWithConstraints classEnv' cond-        thenExpr' <- expandTIExprWithConstraints classEnv' thenExpr-        elseExpr' <- expandTIExprWithConstraints classEnv' elseExpr-        return $ TIIfExpr cond' thenExpr' elseExpr'--      -- Let bindings-      TILetExpr bindings body -> do-        bindings' <- mapM (\(v, e) -> do-          e' <- expandTIExprWithConstraints classEnv' e-          return (v, e')) bindings-        body' <- expandTIExprWithConstraints classEnv' body-        return $ TILetExpr bindings' body'--      TILetRecExpr bindings body -> do-        bindings' <- mapM (\(v, e) -> do-          e' <- expandTIExprWithConstraints classEnv' e-          return (v, e')) bindings-        body' <- expandTIExprWithConstraints classEnv' body-        return $ TILetRecExpr bindings' body'--      TISeqExpr e1 e2 -> do-        e1' <- expandTIExprWithConstraints classEnv' e1-        e2' <- expandTIExprWithConstraints classEnv' e2-        return $ TISeqExpr e1' e2'--      -- Collections-      TIConsExpr h t -> do-        h' <- expandTIExprWithConstraints classEnv' h-        t' <- expandTIExprWithConstraints classEnv' t-        return $ TIConsExpr h' t'--      TIJoinExpr l r -> do-        l' <- expandTIExprWithConstraints classEnv' l-        r' <- expandTIExprWithConstraints classEnv' r-        return $ TIJoinExpr l' r'-      -      TIHashExpr pairs -> do-        -- Dictionary hashes: process keys but NOT values-        -- Values should remain as simple method references-        pairs' <- mapM (\(k, v) -> do-          k' <- expandTIExprWithConstraints classEnv' k-          -- Do NOT process v - dictionary values should not be expanded-          return (k', v)) pairs-        return $ TIHashExpr pairs'--      TIVectorExpr exprs -> do-        exprs' <- mapM (expandTIExprWithConstraints classEnv') exprs-        return $ TIVectorExpr exprs'--      -- More lambda-like constructs-      TIMemoizedLambdaExpr vars body -> do-        body' <- expandTIExprWithConstraints classEnv' body-        return $ TIMemoizedLambdaExpr vars body'--      TICambdaExpr var body -> do-        body' <- expandTIExprWithConstraints classEnv' body-        return $ TICambdaExpr var body'--      TIWithSymbolsExpr syms body -> do-        body' <- expandTIExprWithConstraints classEnv' body-        return $ TIWithSymbolsExpr syms body'--      TIDoExpr bindings body -> do-        bindings' <- mapM (\(v, e) -> do-          e' <- expandTIExprWithConstraints classEnv' e-          return (v, e')) bindings-        body' <- expandTIExprWithConstraints classEnv' body-        return $ TIDoExpr bindings' body'--      -- Pattern matching-      TIMatchExpr mode target matcher clauses -> do-        target' <- expandTIExprWithConstraints classEnv' target-        matcher' <- expandTIExprWithConstraints classEnv' matcher-        clauses' <- mapM (\(pat, body) -> do-          pat' <- expandTIPattern classEnv' pat-          body' <- expandTIExprWithConstraints classEnv' body-          return (pat', body')) clauses-        return $ TIMatchExpr mode target' matcher' clauses'--      TIMatchAllExpr mode target matcher clauses -> do-        target' <- expandTIExprWithConstraints classEnv' target-        matcher' <- expandTIExprWithConstraints classEnv' matcher-        clauses' <- mapM (\(pat, body) -> do-          pat' <- expandTIPattern classEnv' pat-          body' <- expandTIExprWithConstraints classEnv' body-          return (pat', body')) clauses-        return $ TIMatchAllExpr mode target' matcher' clauses'--      -- Tensor operations-      TITensorMapExpr func tensor -> do-        func' <- expandTIExprWithConstraints classEnv' func-        tensor' <- expandTIExprWithConstraints classEnv' tensor-        return $ TITensorMapExpr func' tensor'--      TITensorMap2Expr func t1 t2 -> do-        func' <- expandTIExprWithConstraints classEnv' func-        t1' <- expandTIExprWithConstraints classEnv' t1-        t2' <- expandTIExprWithConstraints classEnv' t2-        return $ TITensorMap2Expr func' t1' t2'--      TITensorMap2WedgeExpr func t1 t2 -> do-        func' <- expandTIExprWithConstraints classEnv' func-        t1' <- expandTIExprWithConstraints classEnv' t1-        t2' <- expandTIExprWithConstraints classEnv' t2-        return $ TITensorMap2WedgeExpr func' t1' t2'--      TIGenerateTensorExpr func shape -> do-        func' <- expandTIExprWithConstraints classEnv' func-        shape' <- expandTIExprWithConstraints classEnv' shape-        return $ TIGenerateTensorExpr func' shape'--      TITensorExpr shape elems -> do-        shape' <- expandTIExprWithConstraints classEnv' shape-        elems' <- expandTIExprWithConstraints classEnv' elems-        return $ TITensorExpr shape' elems'--      TITensorContractExpr tensor -> do-        tensor' <- expandTIExprWithConstraints classEnv' tensor-        return $ TITensorContractExpr tensor'--      TITransposeExpr perm tensor -> do-        perm' <- expandTIExprWithConstraints classEnv' perm-        tensor' <- expandTIExprWithConstraints classEnv' tensor-        return $ TITransposeExpr perm' tensor'--      TIFlipIndicesExpr tensor -> do-        tensor' <- expandTIExprWithConstraints classEnv' tensor-        return $ TIFlipIndicesExpr tensor'--      -- Quote expressions-      TIQuoteExpr e -> do-        e' <- expandTIExprWithConstraints classEnv' e-        return $ TIQuoteExpr e'--      TIQuoteSymbolExpr e -> do-        e' <- expandTIExprWithConstraints classEnv' e-        return $ TIQuoteSymbolExpr e'--      -- Indexed expressions-      TISubrefsExpr b base ref -> do-        base' <- expandTIExprWithConstraints classEnv' base-        ref' <- expandTIExprWithConstraints classEnv' ref-        return $ TISubrefsExpr b base' ref'-      -      TISuprefsExpr b base ref -> do-        base' <- expandTIExprWithConstraints classEnv' base-        ref' <- expandTIExprWithConstraints classEnv' ref-        return $ TISuprefsExpr b base' ref'--      TIUserrefsExpr b base ref -> do-        base' <- expandTIExprWithConstraints classEnv' base-        ref' <- expandTIExprWithConstraints classEnv' ref-        return $ TIUserrefsExpr b base' ref'--      -- Other cases: return unchanged for now-      TIInductiveDataExpr name exprs -> do-        exprs' <- mapM (expandTIExprWithConstraints classEnv') exprs-        return $ TIInductiveDataExpr name exprs'--      TIMatcherExpr patDefs -> do-        -- Expand expressions inside matcher definitions-        -- patDefs is a list of (PrimitivePatPattern, TIExpr, [TIBindingExpr])-        -- where TIBindingExpr is (IPrimitiveDataPattern, TIExpr)-        patDefs' <- mapM (\(pat, matcherExpr, bindings) -> do-          -- Expand the next-matcher expression-          matcherExpr' <- expandTIExprWithConstraints classEnv' matcherExpr-          -- Expand expressions in primitive-data-match clauses-          bindings' <- mapM (\(dp, expr) -> do-            expr' <- expandTIExprWithConstraints classEnv' expr-            return (dp, expr')) bindings-          return (pat, matcherExpr', bindings')) patDefs-        return $ TIMatcherExpr patDefs'-      TIIndexedExpr override base indices -> do-        base' <- expandTIExprWithConstraints classEnv' base-        -- Expand indices (which are already typed as TIExpr)-        indices' <- mapM (traverse (\tiexpr -> expandTIExprWithConstraints classEnv' tiexpr)) indices-        return $ TIIndexedExpr override base' indices'--      TIWedgeApplyExpr func args -> do-        func' <- expandTIExprWithConstraints classEnv' func-        args' <- mapM (expandTIExprWithConstraints classEnv') args-        return $ TIWedgeApplyExpr func' args'-      -      TIFunctionExpr names -> return $ TIFunctionExpr names  -- Built-in function, no expansion needed-    -    -- Helper: expand a TIExpr using only its own constraints-    -- Parent constraints are not passed to avoid constraint accumulation-    expandTIExprWithConstraints :: ClassEnv -> TIExpr -> EvalM TIExpr-    expandTIExprWithConstraints classEnv' expr = do-      let scheme@(Forall _ exprConstraints exprType) = tiScheme expr-          -- Use only the expression's own constraints-          -- Type inference has already assigned correct constraints to each expression-          allConstraints = exprConstraints--      -- Special handling for TIVarExpr: eta-expand methods or apply dictionaries-      expandedNode <- case tiExprNode expr of-        TIVarExpr varName -> do-          -- Check if this is a type class method-          case findConstraintForMethod classEnv' varName allConstraints of-            Just (Constraint className tyArg) -> do-              -- Get method type to determine arity-              typeEnv <- getTypeEnv-              case lookupEnv (stringToVar varName) typeEnv of-                Just (Forall _ _ _ty) -> do-                  -- Use the expression's actual type (exprType) instead of the method's declared type (ty)-                  -- because eta-expansion should create parameters matching the expected usage context-                  let arity = getMethodArity exprType-                      paramTypes = getParamTypes exprType-                      paramNames = ["etaVar" ++ show i | i <- [1..arity]]-                      paramVars = map stringToVar paramNames-                      paramExprs = zipWith (\n t -> TIExpr (Forall [] [] t) (TIVarExpr n)) paramNames paramTypes-                      methodKey = sanitizeMethodName varName-                  -                  -- Determine dictionary name based on type-                  case tyArg of-                    TVar (TyVar _v) -> do-                      -- Type variable: use dictionary parameter name (without type parameter)-                      typeEnv <- getTypeEnv-                      let dictParamName = "dict_" ++ className-                      -- Look up dictionary type from type environment-                      dictHashType <- case lookupEnv (stringToVar dictParamName) typeEnv of-                        Just (Forall _ _ dictType) -> return dictType-                        Nothing -> return $ THash TString TAny  -- Fallback-                      -- Get method type from ClassEnv instead of dictHashType-                      let methodType = getMethodTypeFromClass classEnv' className methodKey tyArg-                          methodConstraint = Constraint className tyArg-                          methodScheme = Forall (Set.toList $ freeTyVars tyArg) [methodConstraint] methodType-                          dictExpr = TIExpr (Forall [] [] dictHashType) (TIVarExpr dictParamName)-                          indexExpr = TIExpr (Forall [] [] TString)-                                            (TIConstantExpr (StringExpr (pack methodKey)))-                          dictAccess = TIExpr methodScheme $-                                       TIIndexedExpr False dictExpr [Sub indexExpr]-                          -- Calculate result type after applying all parameters-                          resultType = applyParamsToType methodType (length paramExprs)-                          -- Fully applied results don't need constraints-                          bodyScheme = case resultType of-                                         TFun _ _ -> methodScheme  -- Partial application-                                         _ -> Forall [] [] resultType  -- Fully applied: no constraints-                          body = TIExpr bodyScheme (TIApplyExpr dictAccess paramExprs)-                      return $ TILambdaExpr Nothing paramVars body-                    _ -> do-                      -- Concrete type: find matching instance-                      let instances = lookupInstances className classEnv'-                      case findMatchingInstanceForType tyArg instances of-                        Just inst -> do-                          -- Found instance: eta-expand with concrete dictionary-                          typeEnv <- getTypeEnv-                          let instTypeName = typeConstructorName (instType inst)-                              dictName = lowerFirst className ++ instTypeName--                          -- Look up dictionary type from type environment-                          dictHashType <- case lookupEnv (stringToVar dictName) typeEnv of-                            Just (Forall _ _ dictType) -> return dictType-                            Nothing -> return $ THash TString TAny  -- Fallback--                          -- Get method type from ClassEnv instead of dictHashType-                          let methodType = getMethodTypeFromClass classEnv' className methodKey tyArg-                              methodConstraint = Constraint className tyArg-                              methodScheme = Forall (Set.toList $ freeTyVars tyArg) [methodConstraint] methodType--                          -- Check if instance has nested constraints-                          dictExprBase <- if null (instContext inst)-                            then do-                              -- No constraints: dictionary is a simple hash-                              return $ TIExpr (Forall [] [] dictHashType) (TIVarExpr dictName)-                            else do-                              -- Has constraints: dictionary is a function that returns a hash-                              -- Get the result type (should be the hash type after applying arguments)-                              let dictFuncType = case dictHashType of-                                    TFun _ resultType -> TFun dictHashType resultType-                                    _ -> TFun (THash TString TAny) dictHashType-                                  dictFuncExpr = TIExpr (Forall [] [] dictFuncType) (TIVarExpr dictName)-                              dictArgs <- mapM (resolveDictionaryArg classEnv') (instContext inst)-                              return $ TIExpr (Forall [] [] dictHashType) (TIApplyExpr dictFuncExpr dictArgs)--                          let indexExpr = TIExpr (Forall [] [] TString)-                                               (TIConstantExpr (StringExpr (pack methodKey)))-                              dictAccess = TIExpr methodScheme $-                                           TIIndexedExpr False dictExprBase [Sub indexExpr]-                              -- Calculate result type after applying all parameters-                              resultType = applyParamsToType methodType (length paramExprs)-                              -- Fully applied results don't need constraints-                              bodyScheme = case resultType of-                                             TFun _ _ -> methodScheme  -- Partial application-                                             _ -> Forall [] [] resultType  -- Fully applied: no constraints-                              body = TIExpr bodyScheme (TIApplyExpr dictAccess paramExprs)-                          return $ TILambdaExpr Nothing paramVars body-                        Nothing -> checkConstrainedVariable-                Nothing -> checkConstrainedVariable-            Nothing -> checkConstrainedVariable-          where-            -- Check if this is a constrained variable (not a method)-            -- IMPORTANT: Only apply dictionaries if the variable was DEFINED with constraints,-            -- not just if the expression has propagated constraints from usage context.-            checkConstrainedVariable = do-              typeEnv <- getTypeEnv-              -- Look up the variable's original type scheme from TypeEnv-              case lookupEnv (stringToVar varName) typeEnv of-                Just (Forall _ originalConstraints _)-                  | not (null originalConstraints) -> do-                      -- Variable was defined with constraints - apply dictionaries-                      -- Check if all constraints are on concrete types-                      let hasOnlyConcreteConstraints = all isConcreteConstraint exprConstraints-                      if hasOnlyConcreteConstraints-                        then do-                          -- This is a constrained variable with concrete types - apply dictionaries-                          dictArgs <- mapM (resolveDictionaryArg classEnv') exprConstraints-                          -- Create application: varName dict1 dict2 ...-                          let varExpr = TIExpr scheme (TIVarExpr varName)-                          return $ TIApplyExpr varExpr dictArgs-                        else do-                          -- Has type variable constraints - pass dictionary parameters-                          -- This handles recursive calls in polymorphic functions-                          -- Generate dictionary argument expressions for each constraint-                          let makeDict c =-                                let dictName = constraintToDictParam c-                                    dictType = TVar (TyVar "dict")-                                in TIExpr (Forall [] [] dictType) (TIVarExpr dictName)-                              dictArgs = map makeDict exprConstraints-                              varExpr = TIExpr scheme (TIVarExpr varName)-                          return $ TIApplyExpr varExpr dictArgs-                _ ->-                  -- Variable was defined without constraints, or not found in TypeEnv-                  -- Don't apply dictionaries - just process normally-                  expandTIExprNode classEnv' (tiExprNode expr)--            isConcreteConstraint (Constraint _ (TVar _)) = False-            isConcreteConstraint _ = True-        _ -> expandTIExprNode classEnv' (tiExprNode expr)--      return $ TIExpr scheme expandedNode--    -- Expand type class methods in patterns (no parent constraints)-    expandTIPattern :: ClassEnv -> TIPattern -> EvalM TIPattern-    expandTIPattern classEnv' (TIPattern scheme node) = do-      node' <- expandTIPatternNode classEnv' node-      return $ TIPattern scheme node'--    -- Expand pattern nodes recursively (no parent constraints)-    expandTIPatternNode :: ClassEnv -> TIPatternNode -> EvalM TIPatternNode-    expandTIPatternNode classEnv' node = case node of-      -- Loop pattern: expand the loop range expressions-      TILoopPat var loopRange pat1 pat2 -> do-        loopRange' <- expandTILoopRange classEnv' loopRange-        pat1' <- expandTIPattern classEnv' pat1-        pat2' <- expandTIPattern classEnv' pat2-        return $ TILoopPat var loopRange' pat1' pat2'--      -- Recursive pattern constructors-      TIAndPat pat1 pat2 -> do-        pat1' <- expandTIPattern classEnv' pat1-        pat2' <- expandTIPattern classEnv' pat2-        return $ TIAndPat pat1' pat2'--      TIOrPat pat1 pat2 -> do-        pat1' <- expandTIPattern classEnv' pat1-        pat2' <- expandTIPattern classEnv' pat2-        return $ TIOrPat pat1' pat2'--      TIForallPat pat1 pat2 -> do-        pat1' <- expandTIPattern classEnv' pat1-        pat2' <- expandTIPattern classEnv' pat2-        return $ TIForallPat pat1' pat2'--      TINotPat pat -> do-        pat' <- expandTIPattern classEnv' pat-        return $ TINotPat pat'--      TITuplePat pats -> do-        pats' <- mapM (expandTIPattern classEnv') pats-        return $ TITuplePat pats'--      TIInductivePat name pats -> do-        pats' <- mapM (expandTIPattern classEnv') pats-        return $ TIInductivePat name pats'--      TIIndexedPat pat exprs -> do-        pat' <- expandTIPattern classEnv' pat-        exprs' <- mapM (expandTIExprWithConstraints classEnv') exprs-        return $ TIIndexedPat pat' exprs'--      TILetPat bindings pat -> do-        pat' <- expandTIPattern classEnv' pat-        return $ TILetPat bindings pat'  -- TODO: Expand binding expressions-      -      TIPApplyPat funcExpr argPats -> do-        funcExpr' <- expandTIExprWithConstraints classEnv' funcExpr-        argPats' <- mapM (expandTIPattern classEnv') argPats-        return $ TIPApplyPat funcExpr' argPats'--      TIDApplyPat pat pats -> do-        pat' <- expandTIPattern classEnv' pat-        pats' <- mapM (expandTIPattern classEnv') pats-        return $ TIDApplyPat pat' pats'--      TISeqConsPat pat1 pat2 -> do-        pat1' <- expandTIPattern classEnv' pat1-        pat2' <- expandTIPattern classEnv' pat2-        return $ TISeqConsPat pat1' pat2'--      TISeqNilPat -> return TISeqNilPat--      TIVarPat name -> return $ TIVarPat name--      TIInductiveOrPApplyPat name pats -> do-        pats' <- mapM (expandTIPattern classEnv') pats-        return $ TIInductiveOrPApplyPat name pats'--      -- Leaf patterns: no expansion needed-      TIWildCard -> return TIWildCard-      TIPatVar name -> return $ TIPatVar name-      TIValuePat expr -> do-        expr' <- expandTIExprWithConstraints classEnv' expr-        return $ TIValuePat expr'-      TIPredPat pred -> do-        pred' <- expandTIExprWithConstraints classEnv' pred-        return $ TIPredPat pred'-      TIContPat -> return TIContPat-      TILaterPatVar -> return TILaterPatVar--    -- Expand loop range expressions (no parent constraints)-    expandTILoopRange :: ClassEnv -> TILoopRange -> EvalM TILoopRange-    expandTILoopRange classEnv' (TILoopRange start end rangePat) = do-      start' <- expandTIExprWithConstraints classEnv' start-      end' <- expandTIExprWithConstraints classEnv' end-      rangePat' <- expandTIPattern classEnv' rangePat-      return $ TILoopRange start' end' rangePat'--    -- Try to resolve a method call using type class constraints-    -- Dictionary passing: convert method calls to dictionary access-    tryResolveMethodCall :: ClassEnv -> [Constraint] -> String -> [TIExpr] -> EvalM (Maybe TIExprNode)-    tryResolveMethodCall classEnv' cs methodName expandedArgs = do-      -- Find a constraint that provides this method-      case findConstraintForMethod classEnv' methodName cs of-        Nothing -> return Nothing-        Just (Constraint className tyArg) -> do-          -- Look up the class to check if methodName is a method-          case lookupClass className classEnv' of-            Just classInfo -> do-              if methodName `elem` map fst (classMethods classInfo)-                then do-                  let methodKey = sanitizeMethodName methodName-                  -- Check if this is a type variable constraint-                  case tyArg of-                    TVar (TyVar _v) -> do-                      -- Type variable: use dictionary parameter-                      -- e.g., for {Eq a}, use dict_Eq (without type parameter)-                      typeEnv <- getTypeEnv-                      let dictParamName = "dict_" ++ className-                      -- Look up dictionary type from type environment-                      dictHashType <- case lookupEnv (stringToVar dictParamName) typeEnv of-                        Just (Forall _ _ dictType) -> return dictType-                        Nothing -> return $ THash TString TAny  -- Fallback-                      -- Get method type from ClassEnv instead of dictHashType-                      let methodType = getMethodTypeFromClass classEnv' className methodKey tyArg-                          -- No constraints: dictionary access resolves the constraint-                          methodScheme = Forall [] [] methodType-                          dictExpr = TIExpr (Forall [] [] dictHashType) (TIVarExpr dictParamName)-                          indexExpr = TIExpr (Forall [] [] TString) -                                            (TIConstantExpr (StringExpr (pack methodKey)))-                          dictAccess = TIExpr methodScheme $-                                       TIIndexedExpr False dictExpr [Sub indexExpr]-                      -- Apply arguments: dictAccess arg1 arg2 ...-                      return $ Just $ TIApplyExpr dictAccess expandedArgs-                    _ -> do-                      -- Concrete type: try to find matching instance-                      let instances = lookupInstances className classEnv'-                      -- Use actual argument type if needed-                      let argTypes = map tiExprType expandedArgs-                          actualType = case (tyArg, argTypes) of-                            (TVar _, (t:_)) -> t  -- Use first argument's type-                            _ -> tyArg-                      -- Check if actualType is still a type variable-                      case actualType of-                        TVar (TyVar _v') -> do-                          -- Still a type variable: use dictionary parameter-                          typeEnv <- getTypeEnv-                          let dictParamName = "dict_" ++ className-                          -- Look up dictionary type from type environment-                          dictHashType <- case lookupEnv (stringToVar dictParamName) typeEnv of-                            Just (Forall _ _ dictType) -> return dictType-                            Nothing -> return $ THash TString TAny  -- Fallback-                          -- Get method type from ClassEnv instead of dictHashType-                          let methodType = getMethodTypeFromClass classEnv' className methodKey actualType-                              -- No constraints: dictionary access resolves the constraint-                              methodScheme = Forall [] [] methodType-                              dictExpr = TIExpr (Forall [] [] dictHashType) (TIVarExpr dictParamName)-                              indexExpr = TIExpr (Forall [] [] TString) -                                                (TIConstantExpr (StringExpr (pack methodKey)))-                              dictAccess = TIExpr methodScheme $-                                           TIIndexedExpr False dictExpr [Sub indexExpr]-                          -- Apply arguments: dictAccess arg1 arg2 ...-                          return $ Just $ TIApplyExpr dictAccess expandedArgs-                        _ -> case findMatchingInstanceForType actualType instances of-                          Just inst -> do-                            -- Found an instance: generate dictionary access-                            -- e.g., numInteger_"plus" for Num Integer instance-                            typeEnv <- getTypeEnv-                            let instTypeName = typeConstructorName (instType inst)-                                dictName = lowerFirst className ++ instTypeName--                            -- Look up dictionary type from type environment-                            dictHashType <- case lookupEnv (stringToVar dictName) typeEnv of-                              Just (Forall _ _ dictType) -> return dictType-                              Nothing -> return $ THash TString TAny  -- Fallback--                            -- Get method type from ClassEnv instead of dictHashType-                            let methodType = getMethodTypeFromClass classEnv' className methodKey actualType-                                -- No constraints: dictionary access resolves the constraint-                                methodScheme = Forall [] [] methodType-                            -                            -- Check if instance has nested constraints-                            -- If so, dictionary is a function that takes dict parameters-                            dictExprBase <- if null (instContext inst)-                                  then do-                                    -- No constraints: dictionary is a simple hash-                                    let dictExpr = TIExpr (Forall [] [] dictHashType) (TIVarExpr dictName)-                                    return dictExpr-                                  else do-                                    -- Has constraints: dictionary is a function-                                    -- Need to resolve constraint arguments and apply them-                                    -- e.g., eqCollection eqInteger-                                    let dictFuncType = case dictHashType of-                                          TFun _ resultType -> TFun dictHashType resultType-                                          _ -> TFun (THash TString TAny) dictHashType-                                        dictFuncExpr = TIExpr (Forall [] [] dictFuncType) (TIVarExpr dictName)--                                    -- Substitute type variables in constraints with actual types-                                    -- e.g., for instance {Eq a} Eq [a] matched with [Integer]-                                    -- instType inst = [a], actualType = [Integer]-                                    -- constraint {Eq a} should become {Eq Integer}-                                    -- Substitute type variables in constraints-                                    -- e.g., instance {Eq a} Eq [a] matched with [[Integer]]-                                    -- instType = [a], actualType = [[Integer]]-                                    -- Extract a -> [Integer], apply to {Eq a} -> {Eq [Integer]}-                                    let substitutedConstraints = substituteInstanceConstraints (instType inst) actualType (instContext inst)-                                    -- Resolve each substituted constraint (depth is managed internally)-                                    dictArgs <- mapM (resolveDictionaryArg classEnv') substitutedConstraints-                                    -- Apply dictionary function to constraint dictionaries-                                    return $ TIExpr (Forall [] [] dictHashType) (TIApplyExpr dictFuncExpr dictArgs)--                                -- Now index into the dictionary (which is now a hash)-                            let indexExpr = TIExpr (Forall [] [] TString) -                                                  (TIConstantExpr (StringExpr (pack methodKey)))-                                dictAccess = TIExpr methodScheme $-                                             TIIndexedExpr False dictExprBase [Sub indexExpr]-                            -- Apply arguments: dictAccess arg1 arg2 ...-                            return $ Just $ TIApplyExpr dictAccess expandedArgs-                          Nothing -> return Nothing-                else return Nothing-            Nothing -> return Nothing-    -    -- Substitute type variables in instance constraints based on actual type-    -- e.g., for instance {Eq a} Eq [a] matched with [[Integer]]-    -- instType = [a], actualType = [[Integer]]-    -- Extract: a -> [Integer], then apply to constraints {Eq a} -> {Eq [Integer]}-    substituteInstanceConstraints :: Type -> Type -> [Constraint] -> [Constraint]-    substituteInstanceConstraints instType actualType constraints =-      let substs = extractTypeSubstitutions instType actualType-      in map (applySubstsToConstraint substs) constraints--    -- Resolve a constraint to a dictionary argument (with depth limit to prevent infinite recursion)-    resolveDictionaryArg :: ClassEnv -> Constraint -> EvalM TIExpr-    resolveDictionaryArg classEnv constraint = resolveDictionaryArgWithDepth classEnv 50 constraint-    -    resolveDictionaryArgWithDepth :: ClassEnv -> Int -> Constraint -> EvalM TIExpr-    resolveDictionaryArgWithDepth _ 0 (Constraint className _) = do-      -- Depth limit reached, return error placeholder-      return $ TIExpr (Forall [] [] (TVar (TyVar "error"))) (TIVarExpr ("dict_" ++ className ++ "_TOO_DEEP"))-    -    resolveDictionaryArgWithDepth classEnv depth (Constraint className tyArg) = do-      case tyArg of-        TVar (TyVar _v) -> do-          -- Type variable: use dictionary parameter name (without type parameter)-          -- e.g., for {Eq a}, return dict_Eq-          let dictParamName = "dict_" ++ className-              dictType = TVar (TyVar "dict")-          return $ TIExpr (Forall [] [] dictType) (TIVarExpr dictParamName)-        _ -> do-          -- Concrete type: try to find matching instance-          let instances = lookupInstances className classEnv-          case findMatchingInstanceForType tyArg instances of-            Just inst -> do-              -- Found instance: generate dictionary name (e.g., "numInteger", "eqCollection")-              let instTypeName = typeConstructorName (instType inst)-                  dictName = lowerFirst className ++ instTypeName-                  dictType = TVar (TyVar "dict")-                  dictExpr = TIExpr (Forall [] [] dictType) (TIVarExpr dictName)-              -              -- Check if this instance has nested constraints-              -- e.g., instance {Eq a} Eq [a] has constraint {Eq a}-              if null (instContext inst)-                then do-                  -- No constraints: return simple dictionary reference-                  return dictExpr-                else do-                  -- Has constraints: need to resolve them and apply to dictionary-                  -- e.g., for Eq [Integer], resolve {Eq Integer} -> eqInteger-                  -- then return: eqCollection eqInteger--                  -- Substitute type variables in constraints with actual types-                  -- e.g., for instance {Eq a} Eq [a] matched with [[Integer]]-                  -- instType inst = [a], tyArg = [[Integer]]-                  -- Extract: a -> [Integer]-                  -- Apply to constraints: {Eq a} -> {Eq [Integer]}-                  let substs = extractTypeSubstitutions (instType inst) tyArg-                      substitutedConstraints = map (applySubstsToConstraint substs) (instContext inst)--                  -- Recursively resolve each constraint with reduced depth-                  dictArgs <- mapM (resolveDictionaryArgWithDepth classEnv (depth - 1)) substitutedConstraints--                  -- Apply dictionary function to resolved dictionaries-                  -- e.g., eqCollection eqInteger (when resolving Eq [Integer])-                  --       eqCollection (eqCollection eqInteger) (when resolving Eq [[Integer]])-                  return $ TIExpr (Forall [] [] dictType) (TIApplyExpr dictExpr dictArgs)-            Nothing -> do-              -- No instance found - this is an error, but return a dummy for now-              return $ TIExpr (Forall [] [] (TVar (TyVar "error"))) (TIVarExpr "undefined")---- | Generate dictionary parameter name from constraint--- Used for both dictionary parameter generation and dictionary argument passing--- Type parameters are not included in the dictionary parameter name-constraintToDictParam :: Constraint -> String-constraintToDictParam (Constraint className _constraintType) =-  "dict_" ++ className---- | Get method type from ClassEnv--- This retrieves the method type from the class definition and substitutes type variables--- Note: methodKey is the sanitized name (e.g., "plus"), but classMethods uses original names (e.g., "+")--- We need to try both the sanitized and original names-getMethodTypeFromClass :: ClassEnv -> String -> String -> Type -> Type-getMethodTypeFromClass classEnv className methodKey constraintType =-  case lookupClass className classEnv of-    Just classInfo ->-      -- Try to find the method by sanitized name first, then try unsanitizing-      case lookup methodKey (classMethods classInfo) `mplus` lookupUnsanitized methodKey (classMethods classInfo) of-        Just classMethodType ->-          -- Substitute class type parameter with actual constraint type-          -- e.g., class Num a has plus : a -> a -> a-          --       constraint Num t0 → plus : t0 -> t0 -> t0-          applySubstsToType [(classParam classInfo, constraintType)] classMethodType-        Nothing -> TAny  -- Method not found in class-    Nothing -> TAny  -- Class not found-  where-    -- Lookup by unsanitizing the method key (reverse of sanitizeMethodName)-    -- e.g., "plus" -> "+", "times" -> "*"-    lookupUnsanitized :: String -> [(String, a)] -> Maybe a-    lookupUnsanitized key methods =-      case unsanitizeMethodName key of-        Just originalName -> lookup originalName methods-        Nothing -> Nothing--    -- Reverse of sanitizeMethodName-    unsanitizeMethodName :: String -> Maybe String-    unsanitizeMethodName "eq" = Just "=="-    unsanitizeMethodName "neq" = Just "/="-    unsanitizeMethodName "lt" = Just "<"-    unsanitizeMethodName "le" = Just "<="-    unsanitizeMethodName "gt" = Just ">"-    unsanitizeMethodName "ge" = Just ">="-    unsanitizeMethodName "plus" = Just "+"-    unsanitizeMethodName "minus" = Just "-"-    unsanitizeMethodName "times" = Just "*"-    unsanitizeMethodName "div" = Just "/"-    unsanitizeMethodName _ = Nothing---- | Add dictionary parameters to a function based on its type scheme constraints--- This transforms constrained functions into dictionary-passing style-addDictionaryParametersT :: TypeScheme -> TIExpr -> EvalM TIExpr-addDictionaryParametersT (Forall _vars constraints _ty) tiExpr-  | null constraints = return tiExpr  -- No constraints, no change-  | otherwise = do-      classEnv <- getClassEnv-      -- Note: No need to resolve Tensor constraints here because TensorMapInsertion-      -- runs before TypeClassExpand, so tensor operations are already handled.-      -- The execution order is: insertTensorMaps -> expandTypeClassMethodsT-      addDictParamsToTIExpr classEnv constraints tiExpr-  where-    -- Add dictionary parameters to a TIExpr-    addDictParamsToTIExpr :: ClassEnv -> [Constraint] -> TIExpr -> EvalM TIExpr-    addDictParamsToTIExpr env cs expr = case tiExprNode expr of-      -- Lambda: add dictionary parameters before regular parameters-      TILambdaExpr mVar params body -> do-        let dictParams = map constraintToDictParam cs-            dictVars = map stringToVar dictParams-        -- Replace method calls in body with dictionary access-        -- BUT: if body is a hash (dictionary), don't process it-        body' <- case tiExprNode body of-                   TIHashExpr _ -> return body  -- Dictionary body, don't process-                   _ -> replaceMethodCallsWithDictAccessT env cs body-        let newNode = TILambdaExpr mVar (dictVars ++ params) body'-        return $ TIExpr (tiScheme expr) newNode-      -      -- Hash (dictionary definition): wrap in lambda AND apply dict params to methods-      -- Dictionary values are method references that need dictionary parameters-      TIHashExpr pairs -> do-        let dictParams = map constraintToDictParam cs-            dictVars = map stringToVar dictParams-            wrapperType = tiExprType expr-        -        -- For each value in the hash (which is a method reference),-        -- if it has constraints, apply dictionary parameters to it-        pairs' <- mapM (\(k, v) -> do-          -- Check if the value (method) has constraints-          typeEnv <- getTypeEnv-          let vNode = tiExprNode v-          case vNode of-            TIVarExpr methodName -> do-              case lookupEnv (stringToVar methodName) typeEnv of-                Just (Forall _ vConstraints _) | not (null vConstraints) -> do-                  -- Method has constraints, apply dictionary parameters-                  let dictArgExprs = map (\p -> TIExpr (Forall [] [] (TVar (TyVar "dict"))) (TIVarExpr p)) dictParams-                      vApplied = TIExpr (tiScheme v) (TIApplyExpr v dictArgExprs)-                  return (k, vApplied)-                _ -> return (k, v)  -- No constraints, keep as-is-            _ -> return (k, v)  -- Not a variable, keep as-is-          ) pairs-        -        let hashExpr' = TIExpr (tiScheme expr) (TIHashExpr pairs')-            newNode = TILambdaExpr Nothing dictVars hashExpr'-            newScheme = Forall [] [] wrapperType-        return $ TIExpr newScheme newNode-      -      -- Not a lambda: wrap in a lambda with dictionary parameters-      _ -> do-        let dictParams = map constraintToDictParam cs-            dictVars = map stringToVar dictParams-        -- Special handling for TIVarExpr: if it's a constrained variable, apply dictionaries-        expr' <- case tiExprNode expr of-          TIVarExpr varName -> do-            -- Check if this variable has constraints that match our constraints-            typeEnv <- getTypeEnv-            case lookupEnv (stringToVar varName) typeEnv of-              Just (Forall _ varConstraints _) | not (null varConstraints) -> do-                -- Check which constraints from varConstraints match parent constraints cs-                let (Forall _ exprConstraints exprType) = tiScheme expr-                    matchingConstraints = filter (\(Constraint eName eType) ->-                          any (\(Constraint pName pType) ->-                            eName == pName && eType == pType) cs) exprConstraints-                if null matchingConstraints-                  then replaceMethodCallsWithDictAccessT env cs expr-                  else do-                    -- Apply matching dictionary parameters-                    let dictArgExprs = map (\p -> TIExpr (Forall [] [] (TVar (TyVar "dict"))) (TIVarExpr p))-                                           (map constraintToDictParam matchingConstraints)-                        varExpr = TIExpr (tiScheme expr) (TIVarExpr varName)-                    return $ TIExpr (tiScheme expr) (TIApplyExpr varExpr dictArgExprs)-              _ -> replaceMethodCallsWithDictAccessT env cs expr-          _ -> replaceMethodCallsWithDictAccessT env cs expr-        let wrapperType = tiExprType expr-            newNode = TILambdaExpr Nothing dictVars expr'-            newScheme = Forall [] [] wrapperType-        return $ TIExpr newScheme newNode-    -    -- Replace method calls with dictionary access in TIExpr-    replaceMethodCallsWithDictAccessT :: ClassEnv -> [Constraint] -> TIExpr -> EvalM TIExpr-    replaceMethodCallsWithDictAccessT env cs tiExpr = do-      let scheme@(Forall _ exprConstraints exprType) = tiScheme tiExpr-      newNode <- replaceMethodCallsInNode env cs exprConstraints exprType (tiExprNode tiExpr)-      return $ TIExpr scheme newNode-    -    -- Replace method calls in TIExprNode-    replaceMethodCallsInNode :: ClassEnv -> [Constraint] -> [Constraint] -> Type -> TIExprNode -> EvalM TIExprNode-    replaceMethodCallsInNode env cs exprConstraints exprType node = case node of-      -- Standalone method reference: eta-expand-      TIVarExpr methodName -> do-        case findConstraintForMethod env methodName cs of-          Just constraint -> do-            -- Get method type to determine arity-            typeEnv <- getTypeEnv-            case lookupEnv (stringToVar methodName) typeEnv of-              Just (Forall _ _ _ty) -> do-                -- Use the expression's actual type (exprType) instead of the method's declared type (ty)-                -- because eta-expansion should create parameters matching the expected usage context-                let arity = getMethodArity exprType-                    paramTypes = getParamTypes exprType-                    paramNames = ["etaVar" ++ show i | i <- [1..arity]]-                    paramVars = map stringToVar paramNames-                    paramExprs = zipWith (\n t -> TIExpr (Forall [] [] t) (TIVarExpr n)) paramNames paramTypes-                    -- Create dictionary access-                    dictParam = constraintToDictParam constraint-                    Constraint className tyArg = constraint-                -- Look up dictionary type from type environment-                dictHashType <- case lookupEnv (stringToVar dictParam) typeEnv of-                  Just (Forall _ _ dictType) -> return dictType-                  Nothing -> return $ THash TString TAny  -- Fallback-                -- Get method type from ClassEnv instead of dictHashType-                let methodType = getMethodTypeFromClass env className (sanitizeMethodName methodName) tyArg-                    methodConstraint = Constraint className tyArg-                    methodScheme = Forall (Set.toList $ freeTyVars tyArg) [methodConstraint] methodType-                    indexExpr = TIExpr (Forall [] [] TString) -                                      (TIConstantExpr (StringExpr (pack (sanitizeMethodName methodName))))-                    dictAccess = TIExpr methodScheme $-                                 TIIndexedExpr False-                                   (TIExpr (Forall [] [] dictHashType) (TIVarExpr dictParam))-                                   [Sub indexExpr]-                    -- Create: dictAccess etaVar1 etaVar2 ... etaVarN-                    body = TIExpr methodScheme (TIApplyExpr dictAccess paramExprs)-                return $ TILambdaExpr Nothing paramVars body-              Nothing -> return $ TIVarExpr methodName-          Nothing -> do-            -- Not a method - just return the variable as-is-            -- Dictionary application for constrained variables is handled by expandTypeClassMethodsT-            return $ TIVarExpr methodName-      -      -- Method call: replace with dictionary access-      TIApplyExpr func args -> do-        case tiExprNode func of-          TIVarExpr methodName -> do-            case findConstraintForMethod env methodName cs of-              Just constraint -> do-                -- Replace with dictionary access-                typeEnv <- getTypeEnv-                let dictParam = constraintToDictParam constraint-                    Constraint className tyArg = constraint-                -- Look up dictionary type from type environment-                dictHashType <- case lookupEnv (stringToVar dictParam) typeEnv of-                  Just (Forall _ _ dictType) -> return dictType-                  Nothing -> return $ THash TString TAny  -- Fallback-                -- Get method type from ClassEnv instead of dictHashType-                let methodType = getMethodTypeFromClass env className (sanitizeMethodName methodName) tyArg-                    methodConstraint = Constraint className tyArg-                    methodScheme = Forall (Set.toList $ freeTyVars tyArg) [methodConstraint] methodType-                    indexExpr = TIExpr (Forall [] [] TString) -                                      (TIConstantExpr (StringExpr (pack (sanitizeMethodName methodName))))-                    dictAccessNode = TIIndexedExpr False-                                     (TIExpr (Forall [] [] dictHashType) (TIVarExpr dictParam))-                                     [Sub indexExpr]-                    dictAccess = TIExpr methodScheme dictAccessNode-                -- Recursively process arguments-                args' <- mapM (replaceMethodCallsWithDictAccessT env cs) args-                return $ TIApplyExpr dictAccess args'-              Nothing -> do-                -- Not a method, process recursively-                func' <- replaceMethodCallsWithDictAccessT env cs func-                args' <- mapM (replaceMethodCallsWithDictAccessT env cs) args-                return $ TIApplyExpr func' args'-          _ -> do-            -- Not a simple variable, process recursively-            func' <- replaceMethodCallsWithDictAccessT env cs func-            args' <- mapM (replaceMethodCallsWithDictAccessT env cs) args-            return $ TIApplyExpr func' args'-      -      -- Lambda: recursively process body-      TILambdaExpr mVar params body -> do-        body' <- replaceMethodCallsWithDictAccessT env cs body-        return $ TILambdaExpr mVar params body'-      -      -- If: recursively process-      TIIfExpr cond thenExpr elseExpr -> do-        cond' <- replaceMethodCallsWithDictAccessT env cs cond-        thenExpr' <- replaceMethodCallsWithDictAccessT env cs thenExpr-        elseExpr' <- replaceMethodCallsWithDictAccessT env cs elseExpr-        return $ TIIfExpr cond' thenExpr' elseExpr'-      -      -- Let: recursively process-      TILetExpr bindings body -> do-        bindings' <- mapM (\(pat, e) -> do-          e' <- replaceMethodCallsWithDictAccessT env cs e-          return (pat, e')) bindings-        body' <- replaceMethodCallsWithDictAccessT env cs body-        return $ TILetExpr bindings' body'-      -      -- LetRec: recursively process-      TILetRecExpr bindings body -> do-        bindings' <- mapM (\(pat, e) -> do-          e' <- replaceMethodCallsWithDictAccessT env cs e-          return (pat, e')) bindings-        body' <- replaceMethodCallsWithDictAccessT env cs body-        return $ TILetRecExpr bindings' body'-      -      -- Hash: do NOT process values inside dictionary hashes-      -- Dictionary values should remain as simple references-      -- e.g., {| ("eq", eqCollectionEq), ... |} not {| ("eq", eqCollectionEq dict_Eq), ... |}-      -- We return the node as-is without recursively processing the pairs-      TIHashExpr pairs -> do-        -- Process only keys, not values (values should remain as method references)-        pairs' <- mapM (\(k, v) -> do-          k' <- replaceMethodCallsWithDictAccessT env cs k-          -- Do NOT process v - keep it as a simple reference-          return (k', v)) pairs-        return $ TIHashExpr pairs'-      -      -- Matcher: recursively process expressions inside matcher definitions-      TIMatcherExpr patDefs -> do-        patDefs' <- mapM (\(pat, matcherExpr, bindings) -> do-          -- Process the next-matcher expression-          matcherExpr' <- replaceMethodCallsWithDictAccessT env cs matcherExpr-          -- Process expressions in primitive-data-match clauses-          bindings' <- mapM (\(dp, expr) -> do-            expr' <- replaceMethodCallsWithDictAccessT env cs expr-            return (dp, expr')) bindings-          return (pat, matcherExpr', bindings')) patDefs-        return $ TIMatcherExpr patDefs'-      -      -- Other expressions: return as-is for now-      _ -> return node---- | Apply dictionaries to expressions with concrete type constraints--- This is used for top-level definitions like: def integer : Matcher Integer := eq--- where the right-hand side (eq) has concrete type constraints {Eq Integer}-applyConcreteConstraintDictionaries :: TIExpr -> EvalM TIExpr-applyConcreteConstraintDictionaries expr = do-  classEnv <- getClassEnv-  let scheme@(Forall vars constraints _) = tiScheme expr--  -- First, recursively process sub-expressions-  expr' <- case tiExprNode expr of-    TIApplyExpr func args -> do-      func' <- applyConcreteConstraintDictionaries func-      args' <- mapM applyConcreteConstraintDictionaries args-      return $ TIExpr scheme (TIApplyExpr func' args')-    _ -> return expr--  -- Then check if this expression has concrete constraints-  let isConcreteConstraint (Constraint _ (TVar _)) = False-      isConcreteConstraint _ = True-      hasOnlyConcreteConstraints = not (null constraints) && all isConcreteConstraint constraints--  if hasOnlyConcreteConstraints-    then do-      -- Apply dictionaries for concrete constraints-      dictArgs <- mapM (resolveDictionaryForConstraint classEnv) constraints-      -- Create application: expr dict1 dict2 ...-      let resultType = tiExprType expr'-          -- Update scheme to remove constraints since they are now applied-          -- Keep type variables (vars) as they may be needed for polymorphism-          newScheme = Forall vars [] resultType-      return $ TIExpr newScheme (TIApplyExpr expr' dictArgs)-    else-      -- No concrete constraints, return as-is-      return expr'-  where-    -- Resolve dictionary for a concrete constraint-    resolveDictionaryForConstraint :: ClassEnv -> Constraint -> EvalM TIExpr-    resolveDictionaryForConstraint classEnv (Constraint className tyArg) = do-      -- Normalize TInt to TMathExpr for instance matching-      -- Integer and MathExpr are the same type in Egison-      let normalizedType = case tyArg of-                             TInt -> TMathExpr-                             _ -> tyArg-      let instances = lookupInstances className classEnv-      case findMatchingInstanceForType normalizedType instances of-        Just inst -> do-          -- Generate dictionary name (e.g., "eqInteger", "numInteger")-          let instTypeName = typeConstructorName (instType inst)-              dictName = lowerFirst className ++ instTypeName-              dictType = TVar (TyVar "dict")-              dictExpr = TIExpr (Forall [] [] dictType) (TIVarExpr dictName)-          -          -- Check if instance has nested constraints-          if null (instContext inst)-            then do-              -- No constraints: return simple dictionary reference-              return dictExpr-            else do-              -- Has constraints: need to resolve them recursively-              nestedDictArgs <- mapM (resolveDictionaryForConstraint classEnv) (instContext inst)-              return $ TIExpr (Forall [] [] dictType) (TIApplyExpr dictExpr nestedDictArgs)-        Nothing -> do-          -- No instance found - return dummy dictionary-          let dictName = "dict_" ++ className ++ "_NOT_FOUND"-              dictType = TVar (TyVar "dict")-          return $ TIExpr (Forall [] [] dictType) (TIVarExpr dictName)---- | Expand type class method calls in patterns--- This is a public wrapper for expandTIPattern used by TypedDesugar-expandTypeClassMethodsInPattern :: TIPattern -> EvalM TIPattern-expandTypeClassMethodsInPattern tipat = do-  classEnv <- getClassEnv-  expandPatternWithClassEnv classEnv tipat-  where-    expandPatternWithClassEnv :: ClassEnv -> TIPattern -> EvalM TIPattern-    expandPatternWithClassEnv classEnv' (TIPattern scheme node) = do-      node' <- expandPatternNode classEnv' node-      return $ TIPattern scheme node'-    -    expandPatternNode :: ClassEnv -> TIPatternNode -> EvalM TIPatternNode-    expandPatternNode classEnv' node = case node of-      TILoopPat var loopRange pat1 pat2 -> do-        loopRange' <- expandLoopRange classEnv' loopRange-        pat1' <- expandPatternWithClassEnv classEnv' pat1-        pat2' <- expandPatternWithClassEnv classEnv' pat2-        return $ TILoopPat var loopRange' pat1' pat2'-      -      TIAndPat pat1 pat2 -> do-        pat1' <- expandPatternWithClassEnv classEnv' pat1-        pat2' <- expandPatternWithClassEnv classEnv' pat2-        return $ TIAndPat pat1' pat2'-      -      TIOrPat pat1 pat2 -> do-        pat1' <- expandPatternWithClassEnv classEnv' pat1-        pat2' <- expandPatternWithClassEnv classEnv' pat2-        return $ TIOrPat pat1' pat2'-      -      TIForallPat pat1 pat2 -> do-        pat1' <- expandPatternWithClassEnv classEnv' pat1-        pat2' <- expandPatternWithClassEnv classEnv' pat2-        return $ TIForallPat pat1' pat2'-      -      TINotPat pat -> do-        pat' <- expandPatternWithClassEnv classEnv' pat-        return $ TINotPat pat'-      -      TITuplePat pats -> do-        pats' <- mapM (expandPatternWithClassEnv classEnv') pats-        return $ TITuplePat pats'-      -      TIInductivePat name pats -> do-        pats' <- mapM (expandPatternWithClassEnv classEnv') pats-        return $ TIInductivePat name pats'-      -      TIIndexedPat pat exprs -> do-        pat' <- expandPatternWithClassEnv classEnv' pat-        exprs' <- mapM expandTypeClassMethodsT exprs-        return $ TIIndexedPat pat' exprs'-      -      TILetPat bindings pat -> do-        pat' <- expandPatternWithClassEnv classEnv' pat-        bindings' <- mapM (\(pd, e) -> do-          e' <- expandTypeClassMethodsT e-          return (pd, e')) bindings-        return $ TILetPat bindings' pat'-      -      TIPApplyPat funcExpr argPats -> do-        funcExpr' <- expandTypeClassMethodsT funcExpr-        argPats' <- mapM (expandPatternWithClassEnv classEnv') argPats-        return $ TIPApplyPat funcExpr' argPats'-      -      TIDApplyPat pat pats -> do-        pat' <- expandPatternWithClassEnv classEnv' pat-        pats' <- mapM (expandPatternWithClassEnv classEnv') pats-        return $ TIDApplyPat pat' pats'-      -      TISeqConsPat pat1 pat2 -> do-        pat1' <- expandPatternWithClassEnv classEnv' pat1-        pat2' <- expandPatternWithClassEnv classEnv' pat2-        return $ TISeqConsPat pat1' pat2'-      -      TIInductiveOrPApplyPat name pats -> do-        pats' <- mapM (expandPatternWithClassEnv classEnv') pats-        return $ TIInductiveOrPApplyPat name pats'-      -      TIValuePat expr -> do-        expr' <- expandTypeClassMethodsT expr-        expr'' <- applyConcreteConstraintDictionaries expr'-        return $ TIValuePat expr''-      -      TIPredPat pred -> do-        pred' <- expandTypeClassMethodsT pred-        pred'' <- applyConcreteConstraintDictionaries pred'-        return $ TIPredPat pred''-      -      -- Leaf patterns-      TISeqNilPat -> return TISeqNilPat-      TIVarPat name -> return $ TIVarPat name-      TIWildCard -> return TIWildCard-      TIPatVar name -> return $ TIPatVar name-      TIContPat -> return TIContPat-      TILaterPatVar -> return TILaterPatVar-    -    expandLoopRange :: ClassEnv -> TILoopRange -> EvalM TILoopRange-    expandLoopRange classEnv' (TILoopRange start end rangePat) = do-      start' <- expandTypeClassMethodsT start-      end' <- expandTypeClassMethodsT end-      rangePat' <- expandPatternWithClassEnv classEnv' rangePat-      return $ TILoopRange start' end' rangePat'---- | Apply dictionaries to expressions with concrete constraints in patterns--- This is used to apply dictionaries to value patterns like #(n + 1)-applyConcreteConstraintDictionariesInPattern :: TIPattern -> EvalM TIPattern-applyConcreteConstraintDictionariesInPattern (TIPattern scheme node) = do-  node' <- applyDictInPatternNode node-  return $ TIPattern scheme node'-  where-    applyDictInPatternNode :: TIPatternNode -> EvalM TIPatternNode-    applyDictInPatternNode pnode = case pnode of-      TIValuePat expr -> do-        expr' <- applyConcreteConstraintDictionaries expr-        return $ TIValuePat expr'-      -      TIPredPat expr -> do-        expr' <- applyConcreteConstraintDictionaries expr-        return $ TIPredPat expr'-      -      TIIndexedPat pat exprs -> do-        pat' <- applyConcreteConstraintDictionariesInPattern pat-        exprs' <- mapM applyConcreteConstraintDictionaries exprs-        return $ TIIndexedPat pat' exprs'-      -      TILetPat bindings pat -> do-        pat' <- applyConcreteConstraintDictionariesInPattern pat-        bindings' <- mapM (\(pd, e) -> do-          e' <- applyConcreteConstraintDictionaries e-          return (pd, e')) bindings-        return $ TILetPat bindings' pat'-      -      TILoopPat var loopRange pat1 pat2 -> do-        loopRange' <- applyDictInLoopRange loopRange-        pat1' <- applyConcreteConstraintDictionariesInPattern pat1-        pat2' <- applyConcreteConstraintDictionariesInPattern pat2-        return $ TILoopPat var loopRange' pat1' pat2'-      -      TIAndPat pat1 pat2 -> do-        pat1' <- applyConcreteConstraintDictionariesInPattern pat1-        pat2' <- applyConcreteConstraintDictionariesInPattern pat2-        return $ TIAndPat pat1' pat2'-      -      TIOrPat pat1 pat2 -> do-        pat1' <- applyConcreteConstraintDictionariesInPattern pat1-        pat2' <- applyConcreteConstraintDictionariesInPattern pat2-        return $ TIOrPat pat1' pat2'-      -      TIForallPat pat1 pat2 -> do-        pat1' <- applyConcreteConstraintDictionariesInPattern pat1-        pat2' <- applyConcreteConstraintDictionariesInPattern pat2-        return $ TIForallPat pat1' pat2'-      -      TINotPat pat -> do-        pat' <- applyConcreteConstraintDictionariesInPattern pat-        return $ TINotPat pat'-      -      TITuplePat pats -> do-        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats-        return $ TITuplePat pats'-      -      TIInductivePat name pats -> do-        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats-        return $ TIInductivePat name pats'-      -      TIPApplyPat funcExpr argPats -> do-        funcExpr' <- applyConcreteConstraintDictionaries funcExpr-        argPats' <- mapM applyConcreteConstraintDictionariesInPattern argPats-        return $ TIPApplyPat funcExpr' argPats'-      -      TIDApplyPat pat pats -> do-        pat' <- applyConcreteConstraintDictionariesInPattern pat-        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats-        return $ TIDApplyPat pat' pats'-      -      TISeqConsPat pat1 pat2 -> do-        pat1' <- applyConcreteConstraintDictionariesInPattern pat1-        pat2' <- applyConcreteConstraintDictionariesInPattern pat2-        return $ TISeqConsPat pat1' pat2'-      -      TIInductiveOrPApplyPat name pats -> do-        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats-        return $ TIInductiveOrPApplyPat name pats'-      -      -- Leaf patterns-      TISeqNilPat -> return TISeqNilPat-      TIVarPat name -> return $ TIVarPat name-      TIWildCard -> return TIWildCard-      TIPatVar name -> return $ TIPatVar name-      TIContPat -> return TIContPat-      TILaterPatVar -> return TILaterPatVar-    -    applyDictInLoopRange :: TILoopRange -> EvalM TILoopRange-    applyDictInLoopRange (TILoopRange start end rangePat) = do-      start' <- applyConcreteConstraintDictionaries start-      end' <- applyConcreteConstraintDictionaries end-      rangePat' <- applyConcreteConstraintDictionariesInPattern rangePat-      return $ TILoopRange start' end' rangePat'+Pipeline: Phase 7 (TypedDesugar) - TypeClassExpand+This is executed after TensorMapInsertion.  TensorMapInsertion must see the+unexpanded qualified type first; this module then resolves type class methods+to concrete dictionary-based functions.++For example, if we have:+  class Eq a where (==) : a -> a -> Bool+  instance Eq Integer where (==) x y := x = y++Then a call like:+  autoEq 1 2  (with type constraint: Eq Integer)+becomes:+  eqIntegerEq 1 2  (dictionary-based dispatch)++This eliminates the need for runtime dispatch functions like resolveEq.+-}++module Language.Egison.Type.TypeClassExpand+  ( expandTypeClassMethodsT+  , expandTypeClassMethodsInPattern+  , addDictionaryParametersT+  , applyConcreteConstraintDictionaries+  , applyConcreteConstraintDictionariesInPattern+  , fixUnboundDictRefs+  ) where++import           Data.Char                  (toLower)+import           Data.List                  (isPrefixOf)+import           Data.Maybe                 (mapMaybe)+import           Data.Text                  (pack, unpack)+import           Control.Monad              (mplus)+import qualified Data.Set                   as Set++import           Language.Egison.AST        (ConstantExpr(..))+import           Language.Egison.Data       (EvalM)+import           Language.Egison.EvalState  (MonadEval(..))+import           Language.Egison.IExpr      (TIExpr(..), TIExprNode(..), stringToVar,+                                             Index(..), tiExprType, tiScheme, tiExprNode,+                                             TIPattern(..), TIPatternNode(..), TILoopRange(..),+                                             IPrimitiveDataPattern,+                                             mapTIExprChildren, Var(..))+import           Language.Egison.Type.Env  (ClassEnv(..), ClassInfo(..), InstanceInfo(..),+                                             lookupInstances, lookupClass, lookupEnv)+import           Language.Egison.Type.Types (Type(..), TyVar(..), TypeScheme(..), Constraint(..), constraintType, typeToName,+                                            sanitizeMethodName, freeTyVars, instType, classParam, mapType)+import           Language.Egison.Type.Instance (findMatchingInstanceForTypes,+                                                  findMostSpecificInstanceForTypes)++-- ============================================================================+-- Helper Functions (shared across the module)+-- ============================================================================++-- | Dispatch helper: prefer the most-specific instance via subtype partial+-- order; fall back to the first unifying instance if specificity returns+-- an ambiguity error or no match. This keeps backward compatibility while+-- enabling deterministic specialization (see design/runtime-type-dispatch.md+-- §4). Runs in EvalM to consult the declared CAS subtype order+-- (`declare cas-subtype` edges) alongside the structural skeleton.+findInstanceForDispatch :: [Type] -> [InstanceInfo] -> EvalM (Maybe InstanceInfo)+findInstanceForDispatch tys insts = do+  edges <- getCasSubtypeEdges+  return $ case findMostSpecificInstanceForTypes edges tys insts of+    Right inst -> Just inst+    Left _     -> findMatchingInstanceForTypes tys insts++-- | Build the candidate list for `TIRuntimeDispatch` from a class's+-- instance list. Each candidate is the instance's principal type paired+-- with the dictionary variable name that holds the instance dictionary.+-- The MathValue instance itself is filtered out so the runtime dispatcher+-- never selects "self" (which would loop). Single-param classes only —+-- multi-param dispatch keeps using static specificity.+runtimeDispatchCandidates :: [InstanceInfo] -> [(Type, String)]+runtimeDispatchCandidates instances =+  [ (instType inst, dictName)+  | inst <- instances+  , length (instTypes inst) == 1+  , instType inst /= TMathValue+  , let dictName = lowerFirst (instClass inst) +++                   concatMap typeToName (instTypes inst)+  ]++-- | Extract type variable substitutions from instance type and actual type+-- Example: [a] -> [[Integer]] gives [(a, [Integer])]+extractTypeSubstitutions :: Type -> Type -> [(TyVar, Type)]+extractTypeSubstitutions instTy actualTy = go instTy actualTy+  where+    go (TVar v) actual = [(v, actual)]+    go (TCollection instElem) (TCollection actualElem) = go instElem actualElem+    go (TTuple instTypes) (TTuple actualTypes)+      | length instTypes == length actualTypes =+          concatMap (\(i, a) -> go i a) (zip instTypes actualTypes)+    go (TInductive _ instArgs) (TInductive _ actualArgs)+      | length instArgs == length actualArgs =+          concatMap (\(i, a) -> go i a) (zip instArgs actualArgs)+    go (TTensor instElem) (TTensor actualElem) = go instElem actualElem+    go (TFun instArg instRet) (TFun actualArg actualRet) =+      go instArg actualArg ++ go instRet actualRet+    go (THash instK instV) (THash actualK actualV) =+      go instK actualK ++ go instV actualV+    go (TMatcher instT) (TMatcher actualT) = go instT actualT+    go (TMatcherSlot instS instT) (TMatcherSlot actualS actualT) = go instS actualS ++ go instT actualT+    go (TIO instT) (TIO actualT) = go instT actualT+    go (TIORef instT) (TIORef actualT) = go instT actualT+    go TPort TPort = []+    go _ _ = []++-- | Apply type substitutions to a constraint+applySubstsToConstraint :: [(TyVar, Type)] -> Constraint -> Constraint+applySubstsToConstraint substs (Constraint cName cTypes) =+  Constraint cName (map (applySubstsToType substs) cTypes)+++-- | Apply type substitutions to a type. Built on 'mapType', so the+-- recursion lives in one place; symbol sets contain atoms, not types, and+-- are left untouched.+applySubstsToType :: [(TyVar, Type)] -> Type -> Type+applySubstsToType substs = mapType replace+  where+    replace t@(TVar v) = case lookup v substs of+                           Just newType -> newType+                           Nothing      -> t+    replace t          = t++-- | Get the arity of a function type (number of parameters)+getMethodArity :: Type -> Int+getMethodArity (TFun _ t2) = 1 + getMethodArity t2+getMethodArity _ = 0++-- | Get parameter types from a function type+getParamTypes :: Type -> [Type]+getParamTypes (TFun t1 t2) = t1 : getParamTypes t2+getParamTypes _ = []++-- | Apply N parameters to a function type and get the result type+-- applyParamsToType (a -> b -> c) 2 = c+-- applyParamsToType (a -> b -> c) 1 = b -> c+applyParamsToType :: Type -> Int -> Type+applyParamsToType (TFun _ t2) n+  | n > 0 = applyParamsToType t2 (n - 1)+applyParamsToType t _ = t  -- n == 0 or no more function types++-- | Lowercase first character of a string+lowerFirst :: String -> String+lowerFirst [] = []+lowerFirst (c:cs) = toLower c : cs++-- | Locally-bound variable names visible at an expression node. Local+-- binders always shadow top-level definitions, so names in this set must+-- never be treated as class methods or as references to constrained+-- top-level functions (e.g. a parameter named `count` must not receive+-- the dictionary arguments of lib's `count {Eq a}`).+type LocalScope = Set.Set String++-- | Names bound by a primitive-data pattern (let/letrec/do binders,+-- matcher primitive-data-match clauses). PDPatternBase is Foldable over+-- its variable slots (PDPatVar), so folding collects exactly the binders.+pdBoundNames :: IPrimitiveDataPattern -> [String]+pdBoundNames pd = [n | Var n _ <- foldr (:) [] pd]++-- | Find a constraint that provides the given method (checks superclass chain).+-- Returns the constraint, the class that owns the method, and the superclass path.+findConstraintForMethod :: ClassEnv -> String -> [Constraint] -> Maybe Constraint+findConstraintForMethod env methodName cs =+  case findConstraintForMethodWithPath env methodName cs of+    Just (constraint, _, _) -> Just constraint+    Nothing -> Nothing++-- | Like findConstraintForMethod but also returns the owning class and superclass path.+findConstraintForMethodWithPath :: ClassEnv -> String -> [Constraint]+                                -> Maybe (Constraint, String, [String])+findConstraintForMethodWithPath env methodName cs = go cs+  where+    go [] = Nothing+    go (c@(Constraint className _) : rest) =+      case findMethodInHierarchy env className methodName of+        Just (ownerClass, path) -> Just (c, ownerClass, path)+        Nothing -> go rest++-- | Search for a method in the class hierarchy starting from a given class.+-- Returns the class that owns the method and the path to it.+findMethodInHierarchy :: ClassEnv -> String -> String -> Maybe (String, [String])+findMethodInHierarchy env startClass methodName = bfs [(startClass, [])]+  where+    bfs [] = Nothing+    bfs ((current, path):queue) =+      case lookupClass current env of+        Nothing -> bfs queue+        Just info ->+          if methodName `elem` map fst (classMethods info)+          then Just (current, path)+          else let nexts = [(s, path ++ [s]) | s <- classSupers info]+               in bfs (queue ++ nexts)++-- ============================================================================+-- Main Type Class Expansion+-- ============================================================================++-- | Expand type class method calls in a typed expression (TIExpr)+-- This function recursively processes TIExpr and replaces type class method calls+-- with dictionary-based dispatch.+expandTypeClassMethodsT :: TIExpr -> EvalM TIExpr+expandTypeClassMethodsT tiExpr = do+  classEnv <- getClassEnv+  -- Use expandTIExprWithConstraints which handles constraint clearing+  expandTIExprWithConstraints classEnv Set.empty tiExpr+  where+    -- Expand TIExprNode without parent constraints+    -- Each child expression uses only its own constraints from type inference+    expandTIExprNode :: ClassEnv -> LocalScope -> TIExprNode -> EvalM TIExprNode+    expandTIExprNode classEnv' scope node = case node of+      -- Constants and variables: no expansion needed at node level+      -- (TIVarExpr expansion is handled at TIExpr level in expandTIExprWithConstraints)+      TIConstantExpr c -> return $ TIConstantExpr c+      TIVarExpr name -> return $ TIVarExpr name+      +      -- Lambda expressions: process body with its own constraints only+      TILambdaExpr mVar params body -> do+        -- Use only the body's own constraints (no parent constraints)+        -- Type inference has already assigned correct constraints to each expression+        let scope' = foldr Set.insert scope+                       ([n | Var n _ <- params] ++ [n | Just (Var n _) <- [mVar]])+        body' <- expandTIExprWithConstraints classEnv' scope' body+        return $ TILambdaExpr mVar params body'+      +      -- Application: check if it's a method call or constrained function call+      TIApplyExpr func args -> do+        -- First, expand the arguments (each uses its own constraints)+        args' <- mapM (expandTIExprWithConstraints classEnv' scope) args++        case tiExprNode func of+          TIVarExpr methodName -> do+            -- Try to resolve if func is a method call using func's own constraints+            let (Forall _ funcConstraints _) = tiScheme func+            resolved <- tryResolveMethodCall classEnv' scope funcConstraints methodName args'+            case resolved of+              Just result -> return result+              Nothing -> do+                -- Not a method call - process recursively+                -- Note: Dictionary application for constrained functions+                -- is handled in TIVarExpr case of expandTIExprWithConstraints+                func' <- expandTIExprWithConstraints classEnv' scope func+                return $ TIApplyExpr func' args'+          _ -> do+            -- Not a simple variable: process recursively+            func' <- expandTIExprWithConstraints classEnv' scope func+            return $ TIApplyExpr func' args'+      +      -- Collections+      TITupleExpr exprs -> do+        exprs' <- mapM (expandTIExprWithConstraints classEnv' scope) exprs+        return $ TITupleExpr exprs'++      TICollectionExpr exprs -> do+        exprs' <- mapM (expandTIExprWithConstraints classEnv' scope) exprs+        return $ TICollectionExpr exprs'++      -- Control flow+      TIIfExpr cond thenExpr elseExpr -> do+        cond' <- expandTIExprWithConstraints classEnv' scope cond+        thenExpr' <- expandTIExprWithConstraints classEnv' scope thenExpr+        elseExpr' <- expandTIExprWithConstraints classEnv' scope elseExpr+        return $ TIIfExpr cond' thenExpr' elseExpr'++      -- Let bindings. `let` is non-recursive (RHSs are evaluated in the+      -- outer environment); `letrec` binders are visible in every RHS.+      TILetExpr bindings body -> do+        bindings' <- mapM (\(v, e) -> do+          e' <- expandTIExprWithConstraints classEnv' scope e+          return (v, e')) bindings+        let scope' = foldr Set.insert scope (concatMap (pdBoundNames . fst) bindings)+        body' <- expandTIExprWithConstraints classEnv' scope' body+        return $ TILetExpr bindings' body'++      TILetRecExpr bindings body -> do+        let scope' = foldr Set.insert scope (concatMap (pdBoundNames . fst) bindings)+        bindings' <- mapM (\(v, e) -> do+          e' <- expandTIExprWithConstraints classEnv' scope' e+          return (v, e')) bindings+        body' <- expandTIExprWithConstraints classEnv' scope' body+        return $ TILetRecExpr bindings' body'++      TISeqExpr e1 e2 -> do+        e1' <- expandTIExprWithConstraints classEnv' scope e1+        e2' <- expandTIExprWithConstraints classEnv' scope e2+        return $ TISeqExpr e1' e2'++      -- Collections+      TIConsExpr h t -> do+        h' <- expandTIExprWithConstraints classEnv' scope h+        t' <- expandTIExprWithConstraints classEnv' scope t+        return $ TIConsExpr h' t'++      TIJoinExpr l r -> do+        l' <- expandTIExprWithConstraints classEnv' scope l+        r' <- expandTIExprWithConstraints classEnv' scope r+        return $ TIJoinExpr l' r'+      +      TIHashExpr pairs -> do+        -- Dictionary hashes: process keys but NOT values+        -- Values should remain as simple method references+        pairs' <- mapM (\(k, v) -> do+          k' <- expandTIExprWithConstraints classEnv' scope k+          -- Do NOT process v - dictionary values should not be expanded+          return (k', v)) pairs+        return $ TIHashExpr pairs'++      TIVectorExpr exprs -> do+        exprs' <- mapM (expandTIExprWithConstraints classEnv' scope) exprs+        return $ TIVectorExpr exprs'++      -- More lambda-like constructs+      TIMemoizedLambdaExpr vars body -> do+        let scope' = foldr Set.insert scope vars+        body' <- expandTIExprWithConstraints classEnv' scope' body+        return $ TIMemoizedLambdaExpr vars body'++      TICambdaExpr var body -> do+        body' <- expandTIExprWithConstraints classEnv' (Set.insert var scope) body+        return $ TICambdaExpr var body'++      TIWithSymbolsExpr syms body -> do+        let scope' = foldr Set.insert scope syms+        body' <- expandTIExprWithConstraints classEnv' scope' body+        return $ TIWithSymbolsExpr syms body'++      -- Do bindings are sequential: each RHS sees the binders of the+      -- previous bindings; the body sees them all.+      TIDoExpr bindings body -> do+        (bindingsRev, scope') <- foldl+          (\acc (v, e) -> do+            (done, sc) <- acc+            e' <- expandTIExprWithConstraints classEnv' sc e+            return ((v, e') : done, foldr Set.insert sc (pdBoundNames v)))+          (return ([], scope)) bindings+        body' <- expandTIExprWithConstraints classEnv' scope' body+        return $ TIDoExpr (reverse bindingsRev) body'++      -- Pattern matching. Pattern variables bind left-to-right: embedded+      -- expressions (value/predicate patterns, loop ranges) see the binders+      -- accumulated so far, and the clause body sees them all.+      TIMatchExpr mode target matcher clauses -> do+        target' <- expandTIExprWithConstraints classEnv' scope target+        matcher' <- expandTIExprWithConstraints classEnv' scope matcher+        clauses' <- mapM (\(pat, body) -> do+          (pat', clauseScope) <- expandTIPattern classEnv' scope pat+          body' <- expandTIExprWithConstraints classEnv' clauseScope body+          return (pat', body')) clauses+        return $ TIMatchExpr mode target' matcher' clauses'++      TIMatchAllExpr mode target matcher clauses -> do+        target' <- expandTIExprWithConstraints classEnv' scope target+        matcher' <- expandTIExprWithConstraints classEnv' scope matcher+        clauses' <- mapM (\(pat, body) -> do+          (pat', clauseScope) <- expandTIPattern classEnv' scope pat+          body' <- expandTIExprWithConstraints classEnv' clauseScope body+          return (pat', body')) clauses+        return $ TIMatchAllExpr mode target' matcher' clauses'++      -- Tensor operations+      TITensorMapExpr func tensor -> do+        func' <- expandTIExprWithConstraints classEnv' scope func+        tensor' <- expandTIExprWithConstraints classEnv' scope tensor+        return $ TITensorMapExpr func' tensor'++      TITensorMap2Expr func t1 t2 -> do+        func' <- expandTIExprWithConstraints classEnv' scope func+        t1' <- expandTIExprWithConstraints classEnv' scope t1+        t2' <- expandTIExprWithConstraints classEnv' scope t2+        return $ TITensorMap2Expr func' t1' t2'++      TITensorMap2WedgeExpr func t1 t2 -> do+        func' <- expandTIExprWithConstraints classEnv' scope func+        t1' <- expandTIExprWithConstraints classEnv' scope t1+        t2' <- expandTIExprWithConstraints classEnv' scope t2+        return $ TITensorMap2WedgeExpr func' t1' t2'++      TIGenerateTensorExpr func shape -> do+        func' <- expandTIExprWithConstraints classEnv' scope func+        shape' <- expandTIExprWithConstraints classEnv' scope shape+        return $ TIGenerateTensorExpr func' shape'++      TITensorExpr shape elems -> do+        shape' <- expandTIExprWithConstraints classEnv' scope shape+        elems' <- expandTIExprWithConstraints classEnv' scope elems+        return $ TITensorExpr shape' elems'++      TITensorContractExpr tensor -> do+        tensor' <- expandTIExprWithConstraints classEnv' scope tensor+        return $ TITensorContractExpr tensor'++      TITransposeExpr perm tensor -> do+        perm' <- expandTIExprWithConstraints classEnv' scope perm+        tensor' <- expandTIExprWithConstraints classEnv' scope tensor+        return $ TITransposeExpr perm' tensor'++      TIFlipIndicesExpr tensor -> do+        tensor' <- expandTIExprWithConstraints classEnv' scope tensor+        return $ TIFlipIndicesExpr tensor'++      -- Quote expressions+      TIQuoteExpr e -> do+        e' <- expandTIExprWithConstraints classEnv' scope e+        return $ TIQuoteExpr e'++      TIQuoteSymbolExpr e -> do+        e' <- expandTIExprWithConstraints classEnv' scope e+        return $ TIQuoteSymbolExpr e'++      -- Indexed expressions+      TISubrefsExpr b base ref -> do+        base' <- expandTIExprWithConstraints classEnv' scope base+        ref' <- expandTIExprWithConstraints classEnv' scope ref+        return $ TISubrefsExpr b base' ref'+      +      TISuprefsExpr b base ref -> do+        base' <- expandTIExprWithConstraints classEnv' scope base+        ref' <- expandTIExprWithConstraints classEnv' scope ref+        return $ TISuprefsExpr b base' ref'++      TIUserrefsExpr b base ref -> do+        base' <- expandTIExprWithConstraints classEnv' scope base+        ref' <- expandTIExprWithConstraints classEnv' scope ref+        return $ TIUserrefsExpr b base' ref'++      -- Other cases: return unchanged for now+      TIInductiveDataExpr name exprs -> do+        exprs' <- mapM (expandTIExprWithConstraints classEnv' scope) exprs+        return $ TIInductiveDataExpr name exprs'++      TIMatcherExpr patDefs -> do+        -- Expand expressions inside matcher definitions+        -- patDefs is a list of (PrimitivePatPattern, TIExpr, [TIBindingExpr])+        -- where TIBindingExpr is (IPrimitiveDataPattern, TIExpr)+        patDefs' <- mapM (\(pat, matcherExpr, bindings) -> do+          -- Expand the next-matcher expression+          matcherExpr' <- expandTIExprWithConstraints classEnv' scope matcherExpr+          -- Expand expressions in primitive-data-match clauses; the+          -- primitive-data pattern's variables are bound in its expression+          bindings' <- mapM (\(dp, expr) -> do+            let scope' = foldr Set.insert scope (pdBoundNames dp)+            expr' <- expandTIExprWithConstraints classEnv' scope' expr+            return (dp, expr')) bindings+          return (pat, matcherExpr', bindings')) patDefs+        return $ TIMatcherExpr patDefs'+      TIIndexedExpr override base indices -> do+        base' <- expandTIExprWithConstraints classEnv' scope base+        -- Expand indices (which are already typed as TIExpr)+        indices' <- mapM (traverse (\tiexpr -> expandTIExprWithConstraints classEnv' scope tiexpr)) indices+        return $ TIIndexedExpr override base' indices'++      TIWedgeApplyExpr func args -> do+        func' <- expandTIExprWithConstraints classEnv' scope func+        args' <- mapM (expandTIExprWithConstraints classEnv' scope) args+        return $ TIWedgeApplyExpr func' args'+      +      TIFunctionExpr names -> return $ TIFunctionExpr names  -- Built-in function, no expansion needed++      -- Runtime dispatch: traverse args; class/method/candidates are already resolved.+      TIRuntimeDispatch className methodName candidates args -> do+        args' <- mapM (expandTIExprWithConstraints classEnv' scope) args+        return $ TIRuntimeDispatch className methodName candidates args'++      -- Reshape: traverse the inner expression; type annotation is metadata.+      TIReshape ty inner -> do+        inner' <- expandTIExprWithConstraints classEnv' scope inner+        return $ TIReshape ty inner'++    -- Helper: expand a TIExpr using only its own constraints+    -- Parent constraints are not passed to avoid constraint accumulation+    expandTIExprWithConstraints :: ClassEnv -> LocalScope -> TIExpr -> EvalM TIExpr+    expandTIExprWithConstraints classEnv' scope expr = do+      let scheme@(Forall _ exprConstraints exprType) = tiScheme expr+          -- Use only the expression's own constraints+          -- Type inference has already assigned correct constraints to each expression+          allConstraints = exprConstraints++      -- Special handling for TIVarExpr: eta-expand methods or apply dictionaries+      expandedNode <- case tiExprNode expr of+        -- Locally bound names (lambda params, let/match binders, ...) always+        -- shadow top-level definitions: treat them as plain variables, never+        -- as class methods or constrained top-level functions.+        TIVarExpr varName | varName `Set.member` scope ->+          expandTIExprNode classEnv' scope (tiExprNode expr)+        TIVarExpr varName -> do+          -- Check if this is a type class method+          case findConstraintForMethod classEnv' varName allConstraints of+            Just constraint@(Constraint className tyArgs) -> do+              -- Principal type: head of tyArgs (the first class type parameter).+              -- For multi-param classes, additional types are in `tyArgs` and used+              -- by `findMatchingInstanceForTypes` for full dispatch.+              let tyArg = constraintType constraint+              -- Get method type to determine arity+              typeEnv <- getTypeEnv+              case lookupEnv (stringToVar varName) typeEnv of+                Just (Forall _ _ _ty) -> do+                  -- Use the expression's actual type (exprType) instead of the method's declared type (ty)+                  -- because eta-expansion should create parameters matching the expected usage context+                  let arity = getMethodArity exprType+                      paramTypes = getParamTypes exprType+                      paramNames = ["etaVar" ++ show i | i <- [1..arity]]+                      paramVars = map stringToVar paramNames+                      paramExprs = zipWith (\n t -> TIExpr (Forall [] [] t) (TIVarExpr n)) paramNames paramTypes+                      methodKey = sanitizeMethodName varName++                  -- Determine dictionary name based on type+                  case tyArg of+                    TVar (TyVar _v) -> do+                      -- Type variable: use dictionary parameter name (without type parameter)+                      typeEnv <- getTypeEnv+                      let dictParamName = "dict_" ++ className+                      -- Look up dictionary type from type environment+                      dictHashType <- case lookupEnv (stringToVar dictParamName) typeEnv of+                        Just (Forall _ _ dictType) -> return dictType+                        Nothing -> return $ THash TString TAny  -- Fallback+                      -- Get method type from ClassEnv instead of dictHashType+                      let methodType = getMethodTypeFromClass classEnv' className methodKey tyArg+                          methodConstraint = Constraint className tyArgs+                          methodScheme = Forall (Set.toList $ freeTyVars tyArg) [methodConstraint] methodType+                          dictExpr = TIExpr (Forall [] [] dictHashType) (TIVarExpr dictParamName)+                          indexExpr = TIExpr (Forall [] [] TString)+                                            (TIConstantExpr (StringExpr (pack methodKey)))+                          dictAccess = TIExpr methodScheme $+                                       TIIndexedExpr False dictExpr [Sub indexExpr]+                      -- 0-arity methods (constants like zero, one): return dict access directly+                      if null paramVars+                        then return $ TIIndexedExpr False dictExpr [Sub indexExpr]+                        else do+                          let resultType = applyParamsToType methodType (length paramExprs)+                              bodyScheme = case resultType of+                                             TFun _ _ -> methodScheme+                                             _ -> Forall [] [] resultType+                              body = TIExpr bodyScheme (TIApplyExpr dictAccess paramExprs)+                          return $ TILambdaExpr Nothing paramVars body+                    _ -> do+                      -- Concrete type: find matching instance using all+                      -- constraint types (single-param classes pass [t]).+                      let instances = lookupInstances className classEnv'+                      mInst <- findInstanceForDispatch tyArgs instances+                      case mInst of+                        Just inst -> do+                          -- Found instance: eta-expand with concrete dictionary+                          typeEnv <- getTypeEnv+                          let instTypeName = concatMap typeToName (instTypes inst)+                              dictName = lowerFirst className ++ instTypeName++                          -- Look up dictionary type from type environment+                          dictHashType <- case lookupEnv (stringToVar dictName) typeEnv of+                            Just (Forall _ _ dictType) -> return dictType+                            Nothing -> return $ THash TString TAny  -- Fallback++                          -- Get method type from ClassEnv instead of dictHashType+                          let methodType = getMethodTypeFromClass classEnv' className methodKey tyArg+                              methodConstraint = Constraint className tyArgs+                              methodScheme = Forall (Set.toList $ freeTyVars tyArg) [methodConstraint] methodType++                          -- Check if instance has nested constraints+                          dictExprBase <- if null (instContext inst)+                            then do+                              -- No constraints: dictionary is a simple hash+                              return $ TIExpr (Forall [] [] dictHashType) (TIVarExpr dictName)+                            else do+                              -- Has constraints: dictionary is a function that returns a hash+                              -- Get the result type (should be the hash type after applying arguments)+                              let dictFuncType = case dictHashType of+                                    TFun _ resultType -> TFun dictHashType resultType+                                    _ -> TFun (THash TString TAny) dictHashType+                                  dictFuncExpr = TIExpr (Forall [] [] dictFuncType) (TIVarExpr dictName)+                              dictArgs <- mapM (resolveDictionaryArg classEnv') (instContext inst)+                              return $ TIExpr (Forall [] [] dictHashType) (TIApplyExpr dictFuncExpr dictArgs)++                          let indexExpr = TIExpr (Forall [] [] TString)+                                               (TIConstantExpr (StringExpr (pack methodKey)))+                              dictAccess = TIExpr methodScheme $+                                           TIIndexedExpr False dictExprBase [Sub indexExpr]+                          -- 0-arity methods (constants like zero, one): return dict access directly+                          if null paramVars+                            then return $ TIIndexedExpr False dictExprBase [Sub indexExpr]+                            else do+                              let resultType = applyParamsToType methodType (length paramExprs)+                                  bodyScheme = case resultType of+                                                 TFun _ _ -> methodScheme+                                                 _ -> Forall [] [] resultType+                                  body = TIExpr bodyScheme (TIApplyExpr dictAccess paramExprs)+                              return $ TILambdaExpr Nothing paramVars body+                        Nothing -> checkConstrainedVariable+                Nothing -> checkConstrainedVariable+            Nothing -> checkConstrainedVariable+          where+            -- Check if this is a constrained variable (not a method)+            -- IMPORTANT: Only apply dictionaries if the variable was DEFINED with constraints,+            -- not just if the expression has propagated constraints from usage context.+            checkConstrainedVariable = do+              typeEnv <- getTypeEnv+              case lookupEnv (stringToVar varName) typeEnv of+                Just (Forall _ originalConstraints _)+                  | not (null originalConstraints) -> do+                      -- De-expand constraints to match the function's dict params+                      let minOrigCs = deExpandConstraints classEnv' originalConstraints+                          hasOnlyConcreteConstraints = all isConcreteConstraint exprConstraints+                      if null minOrigCs+                        then expandTIExprNode classEnv' scope (tiExprNode expr)+                        else if hasOnlyConcreteConstraints+                        then do+                          -- Concrete types: resolve dict args using de-expanded original+                          -- constraints with concrete type substituted+                          let concreteType = case exprConstraints of+                                (c : _) -> constraintType c+                                []      -> TAny+                              -- For TVar constraints (single-param), substitute the concrete type.+                              -- Multi-param constraints with all concrete types pass through unchanged.+                              resolveType c = case constraintTypes c of+                                [TVar _] -> Constraint (constraintClass c) [concreteType]+                                _        -> c+                          dictArgs <- mapM (resolveDictionaryArg classEnv') (map resolveType minOrigCs)+                          -- Clear constraints on the inner var ref to prevent+                          -- applyConcreteConstraintDictionaries from adding dicts again+                          let Forall vs _ ty = scheme+                              varExpr = TIExpr (Forall vs [] ty) (TIVarExpr varName)+                          return $ TIApplyExpr varExpr dictArgs+                        else do+                          -- Type variable: pass dict params for de-expanded constraints+                          let makeDict c =+                                let dictName = constraintToDictParam c+                                    dictType = TVar (TyVar "dict")+                                in TIExpr (Forall [] [] dictType) (TIVarExpr dictName)+                              dictArgs = map makeDict minOrigCs+                              varExpr = TIExpr scheme (TIVarExpr varName)+                          return $ TIApplyExpr varExpr dictArgs+                _ ->+                  expandTIExprNode classEnv' scope (tiExprNode expr)++            isConcreteConstraint c = all isConcreteType (constraintTypes c)+            isConcreteType (TVar _) = False+            isConcreteType _        = True+        _ -> expandTIExprNode classEnv' scope (tiExprNode expr)++      -- If concrete dictionaries were applied by checkConstrainedVariable,+      -- clear constraints from the scheme to prevent applyConcreteConstraintDictionaries+      -- from adding them again (avoiding double-application).+      --+      -- TIApplyExpr: dispatched (n-ary methods, e.g. `(+) x y` → resolved+      -- function applied to args).+      -- TIIndexedExpr: 0-arity method (e.g. `zero` → `dict["zero"]`). Without+      -- this clear, applyConcreteConstraintDictionaries would wrap the value+      -- in `TIApplyExpr value [dict]`, treating the resolved value as a+      -- function and producing "Expected function, but found: 0" at runtime.+      let isConcrete c = all isConcreteType' (constraintTypes c)+          isConcreteType' (TVar _) = False+          isConcreteType' _        = True+          shouldClear = not (null exprConstraints) && all isConcrete exprConstraints+          scheme' = case expandedNode of+            TIApplyExpr _ _    | shouldClear -> let Forall vs _ ty = scheme in Forall vs [] ty+            TIIndexedExpr {}   | shouldClear -> let Forall vs _ ty = scheme in Forall vs [] ty+            _                                -> scheme+      return $ TIExpr scheme' expandedNode++    -- Expand type class methods in patterns (no parent constraints).+    -- Pattern variables bind left-to-right: the returned scope accumulates+    -- the names bound so far, so embedded expressions (value/predicate+    -- patterns, loop ranges) and the clause body see the right binders.+    expandTIPattern :: ClassEnv -> LocalScope -> TIPattern -> EvalM (TIPattern, LocalScope)+    expandTIPattern classEnv' scope (TIPattern scheme node) = do+      (node', scope') <- expandTIPatternNode classEnv' scope node+      return (TIPattern scheme node', scope')++    -- Thread the scope through a list of subpatterns left-to-right.+    expandTIPatterns :: ClassEnv -> LocalScope -> [TIPattern] -> EvalM ([TIPattern], LocalScope)+    expandTIPatterns classEnv' scope pats = go scope [] pats+      where+        go sc acc []       = return (reverse acc, sc)+        go sc acc (p : ps) = do+          (p', sc') <- expandTIPattern classEnv' sc p+          go sc' (p' : acc) ps++    -- Expand pattern nodes recursively (no parent constraints)+    expandTIPatternNode :: ClassEnv -> LocalScope -> TIPatternNode -> EvalM (TIPatternNode, LocalScope)+    expandTIPatternNode classEnv' scope node = case node of+      -- Loop pattern: the loop variable is bound in the subpatterns+      TILoopPat var loopRange pat1 pat2 -> do+        loopRange' <- expandTILoopRange classEnv' scope loopRange+        let scopeV = Set.insert var scope+        (pat1', scope1) <- expandTIPattern classEnv' scopeV pat1+        (pat2', scope2) <- expandTIPattern classEnv' scope1 pat2+        return (TILoopPat var loopRange' pat1' pat2', scope2)++      -- Recursive pattern constructors+      TIAndPat pat1 pat2 -> do+        (pat1', scope1) <- expandTIPattern classEnv' scope pat1+        (pat2', scope2) <- expandTIPattern classEnv' scope1 pat2+        return (TIAndPat pat1' pat2', scope2)++      -- Or-pattern alternatives must bind the same variables; take the+      -- union so the body sees them whichever alternative matched.+      TIOrPat pat1 pat2 -> do+        (pat1', scope1) <- expandTIPattern classEnv' scope pat1+        (pat2', scope2) <- expandTIPattern classEnv' scope pat2+        return (TIOrPat pat1' pat2', Set.union scope1 scope2)++      TIForallPat pat1 pat2 -> do+        (pat1', scope1) <- expandTIPattern classEnv' scope pat1+        (pat2', scope2) <- expandTIPattern classEnv' scope1 pat2+        return (TIForallPat pat1' pat2', scope2)++      -- Not-pattern bindings never escape (a match produces no bindings)+      TINotPat pat -> do+        (pat', _) <- expandTIPattern classEnv' scope pat+        return (TINotPat pat', scope)++      TITuplePat pats -> do+        (pats', scope') <- expandTIPatterns classEnv' scope pats+        return (TITuplePat pats', scope')++      TIInductivePat name pats -> do+        (pats', scope') <- expandTIPatterns classEnv' scope pats+        return (TIInductivePat name pats', scope')++      TIIndexedPat pat exprs -> do+        (pat', scope') <- expandTIPattern classEnv' scope pat+        exprs' <- mapM (expandTIExprWithConstraints classEnv' scope') exprs+        return (TIIndexedPat pat' exprs', scope')++      TILetPat bindings pat -> do+        let scopeB = foldr Set.insert scope (concatMap (pdBoundNames . fst) bindings)+        (pat', scope') <- expandTIPattern classEnv' scopeB pat+        return (TILetPat bindings pat', scope')  -- TODO: Expand binding expressions++      TIPApplyPat funcExpr argPats -> do+        funcExpr' <- expandTIExprWithConstraints classEnv' scope funcExpr+        (argPats', scope') <- expandTIPatterns classEnv' scope argPats+        return (TIPApplyPat funcExpr' argPats', scope')++      TIDApplyPat pat pats -> do+        (pat', scope1) <- expandTIPattern classEnv' scope pat+        (pats', scope') <- expandTIPatterns classEnv' scope1 pats+        return (TIDApplyPat pat' pats', scope')++      TISeqConsPat pat1 pat2 -> do+        (pat1', scope1) <- expandTIPattern classEnv' scope pat1+        (pat2', scope2) <- expandTIPattern classEnv' scope1 pat2+        return (TISeqConsPat pat1' pat2', scope2)++      TISeqNilPat -> return (TISeqNilPat, scope)++      TIVarPat name -> return (TIVarPat name, scope)++      TIInductiveOrPApplyPat name pats -> do+        (pats', scope') <- expandTIPatterns classEnv' scope pats+        return (TIInductiveOrPApplyPat name pats', scope')++      -- Leaf patterns: no expansion needed+      TIWildCard -> return (TIWildCard, scope)+      TIPatVar name -> return (TIPatVar name, Set.insert name scope)+      TIValuePat expr -> do+        expr' <- expandTIExprWithConstraints classEnv' scope expr+        return (TIValuePat expr', scope)+      TIPredPat pred -> do+        pred' <- expandTIExprWithConstraints classEnv' scope pred+        return (TIPredPat pred', scope)+      TIContPat -> return (TIContPat, scope)+      TILaterPatVar -> return (TILaterPatVar, scope)++    -- Expand loop range expressions (no parent constraints)+    expandTILoopRange :: ClassEnv -> LocalScope -> TILoopRange -> EvalM TILoopRange+    expandTILoopRange classEnv' scope (TILoopRange start end rangePat) = do+      start' <- expandTIExprWithConstraints classEnv' scope start+      end' <- expandTIExprWithConstraints classEnv' scope end+      (rangePat', _) <- expandTIPattern classEnv' scope rangePat+      return $ TILoopRange start' end' rangePat'++    -- Try to resolve a method call using type class constraints.+    -- Uses superclass chain traversal to find methods in transitive superclasses.+    -- A locally-bound name is never a class method (local binders shadow).+    tryResolveMethodCall :: ClassEnv -> LocalScope -> [Constraint] -> String -> [TIExpr] -> EvalM (Maybe TIExprNode)+    tryResolveMethodCall _ scope _ methodName _+      | methodName `Set.member` scope = return Nothing+    tryResolveMethodCall classEnv' _ cs methodName expandedArgs = do+      -- De-expand constraints to get the minimal set (matching dict params)+      let minCs = deExpandConstraints classEnv' cs+      case findConstraintForMethodWithPath classEnv' methodName minCs of+        Nothing -> return Nothing+        Just (constraint@(Constraint constraintClass _), ownerClass, path) -> do+          let methodKey = sanitizeMethodName methodName+              dictHashType = THash TString TAny+              tyArg = constraintType constraint   -- principal (first) type+              tyArgs = constraintTypes constraint+          -- Determine the actual type for instance lookup. We refine the first+          -- type from the call's first argument if the constraint's first type+          -- is still a type variable; subsequent multi-param types are unchanged.+          let argTypes = map tiExprType expandedArgs+              actualType = case (tyArg, argTypes) of+                (TVar _, (t:_)) -> t+                _ -> tyArg+              actualTypes = case (tyArgs, argTypes) of+                (TVar _ : rest, t:_) -> t : rest+                _                     -> tyArgs+          case actualType of+            TVar _ -> do+              -- Type variable: use dictionary parameter with superclass chain+              let dictParamName = "dict_" ++ constraintClass+                  dictExpr = TIExpr (Forall [] [] dictHashType) (TIVarExpr dictParamName)+                  chainedDict = buildSuperclassChain dictExpr path+                  methodType = getMethodTypeFromClass classEnv' ownerClass methodKey actualType+                  methodScheme = Forall [] [] methodType+                  indexExpr = TIExpr (Forall [] [] TString)+                                    (TIConstantExpr (StringExpr (pack methodKey)))+                  dictAccess = TIExpr methodScheme $+                               TIIndexedExpr False chainedDict [Sub indexExpr]+              return $ Just $ TIApplyExpr dictAccess expandedArgs+            _ -> do+              -- Concrete type: find matching instance for the ownerClass.+              -- Use full-list dispatch (multi-param-aware).+              let instances = lookupInstances ownerClass classEnv'+              -- Phase 3: if the dispatch target is TMathValue and there is+              -- no explicit `instance Class MathValue`, defer to runtime+              -- dispatch via `TIRuntimeDispatch`. This lets users write only+              -- specific (Frac/Poly/Term/Factor) instances and have the+              -- compiler pick the right one based on the value's CAS shape.+              let mathValueInstanceExists =+                    any (\inst -> instTypes inst == [TMathValue]) instances+                  -- TInt is an alias for TMathValue in Egison's CAS layer+                  -- (see typeToName / typeConstructorName which map both to+                  -- "MathValue"). Treat TInt as MathValue for runtime+                  -- dispatch purposes so `declare symbol`-typed values+                  -- (which surface as TInt) trigger IRuntimeDispatch.+                  isMathValueLike t = t == TMathValue || t == TInt+                  shouldRuntimeDispatch =+                    isMathValueLike actualType+                      && not mathValueInstanceExists+                      && not (null (runtimeDispatchCandidates instances))+              if shouldRuntimeDispatch then do+                let candidates = runtimeDispatchCandidates instances+                return $ Just $+                  TIRuntimeDispatch ownerClass methodKey candidates expandedArgs+              else do+                mInst <- findInstanceForDispatch actualTypes instances+                case mInst of+                  Just inst -> do+                    let instTypeName = concatMap typeToName (instTypes inst)+                        dictName = lowerFirst ownerClass ++ instTypeName+                        methodType = getMethodTypeFromClass classEnv' ownerClass methodKey actualType+                        methodScheme = Forall [] [] methodType+                    dictExprBase <- if null (instContext inst)+                      then return $ TIExpr (Forall [] [] dictHashType) (TIVarExpr dictName)+                      else do+                        let dictFuncType = TFun (THash TString TAny) dictHashType+                            dictFuncExpr = TIExpr (Forall [] [] dictFuncType) (TIVarExpr dictName)+                            substitutedConstraints = substituteInstanceConstraints (instType inst) actualType (instContext inst)+                        dictArgs <- mapM (resolveDictionaryArg classEnv') substitutedConstraints+                        return $ TIExpr (Forall [] [] dictHashType) (TIApplyExpr dictFuncExpr dictArgs)+                    let indexExpr = TIExpr (Forall [] [] TString)+                                          (TIConstantExpr (StringExpr (pack methodKey)))+                        dictAccess = TIExpr methodScheme $+                                     TIIndexedExpr False dictExprBase [Sub indexExpr]+                    return $ Just $ TIApplyExpr dictAccess expandedArgs+                  Nothing -> return Nothing+    +    -- Substitute type variables in instance constraints based on actual type+    -- e.g., for instance {Eq a} Eq [a] matched with [[Integer]]+    -- instType = [a], actualType = [[Integer]]+    -- Extract: a -> [Integer], then apply to constraints {Eq a} -> {Eq [Integer]}+    substituteInstanceConstraints :: Type -> Type -> [Constraint] -> [Constraint]+    substituteInstanceConstraints instType actualType constraints =+      let substs = extractTypeSubstitutions instType actualType+      in map (applySubstsToConstraint substs) constraints++    -- Resolve a constraint to a dictionary argument (with depth limit to prevent infinite recursion)+    resolveDictionaryArg :: ClassEnv -> Constraint -> EvalM TIExpr+    resolveDictionaryArg classEnv constraint = resolveDictionaryArgWithDepth classEnv 50 constraint+    +    resolveDictionaryArgWithDepth :: ClassEnv -> Int -> Constraint -> EvalM TIExpr+    resolveDictionaryArgWithDepth _ 0 (Constraint className _) = do+      -- Depth limit reached, return error placeholder+      return $ TIExpr (Forall [] [] (TVar (TyVar "error"))) (TIVarExpr ("dict_" ++ className ++ "_TOO_DEEP"))+    +    resolveDictionaryArgWithDepth classEnv depth constraint@(Constraint className tyArgs) = do+      let tyArg = constraintType constraint  -- principal (first) type+      case tyArg of+        TVar (TyVar _v) -> do+          -- Type variable: use dictionary parameter name (without type parameter)+          -- e.g., for {Eq a}, return dict_Eq+          let dictParamName = "dict_" ++ className+              dictType = TVar (TyVar "dict")+          return $ TIExpr (Forall [] [] dictType) (TIVarExpr dictParamName)+        _ -> do+          -- Concrete type: find matching instance using all constraint types.+          let instances = lookupInstances className classEnv+          mInst <- findInstanceForDispatch tyArgs instances+          case mInst of+            Just inst -> do+              -- Found instance: generate dictionary name (e.g., "numInteger", "eqCollection")+              let instTypeName = concatMap typeToName (instTypes inst)+                  dictName = lowerFirst className ++ instTypeName+                  dictType = TVar (TyVar "dict")+                  dictExpr = TIExpr (Forall [] [] dictType) (TIVarExpr dictName)+              +              -- Check if this instance has nested constraints+              -- e.g., instance {Eq a} Eq [a] has constraint {Eq a}+              if null (instContext inst)+                then do+                  -- No constraints: return simple dictionary reference+                  return dictExpr+                else do+                  -- Has constraints: need to resolve them and apply to dictionary+                  -- e.g., for Eq [Integer], resolve {Eq Integer} -> eqInteger+                  -- then return: eqCollection eqInteger++                  -- Substitute type variables in constraints with actual types+                  -- e.g., for instance {Eq a} Eq [a] matched with [[Integer]]+                  -- instType inst = [a], tyArg = [[Integer]]+                  -- Extract: a -> [Integer]+                  -- Apply to constraints: {Eq a} -> {Eq [Integer]}+                  let substs = extractTypeSubstitutions (instType inst) tyArg+                      substitutedConstraints = map (applySubstsToConstraint substs) (instContext inst)++                  -- Recursively resolve each constraint with reduced depth+                  dictArgs <- mapM (resolveDictionaryArgWithDepth classEnv (depth - 1)) substitutedConstraints++                  -- Apply dictionary function to resolved dictionaries+                  -- e.g., eqCollection eqInteger (when resolving Eq [Integer])+                  --       eqCollection (eqCollection eqInteger) (when resolving Eq [[Integer]])+                  return $ TIExpr (Forall [] [] dictType) (TIApplyExpr dictExpr dictArgs)+            Nothing -> do+              -- No instance found - this is an error, but return a dummy for now+              return $ TIExpr (Forall [] [] (TVar (TyVar "error"))) (TIVarExpr "undefined")++-- | Generate dictionary parameter name from constraint+-- Used for both dictionary parameter generation and dictionary argument passing+-- Type parameters are not included in the dictionary parameter name+constraintToDictParam :: Constraint -> String+constraintToDictParam (Constraint className _constraintType) =+  "dict_" ++ className++-- | Remove constraints that are transitively implied by other constraints.+-- Given the expanded set {Field a, Ring a, AddGroup a, ...}, returns+-- only the "root" constraints: {Field a}.+deExpandConstraints :: ClassEnv -> [Constraint] -> [Constraint]+deExpandConstraints classEnv cs =+  filter (\c -> not (isSubsumedBy classEnv c cs)) cs+  where+    isSubsumedBy env (Constraint cn ty) allCs =+      any (\(Constraint cn' ty') ->+        cn /= cn' && ty == ty' && isTransitiveSuperclass env cn cn') allCs+    isTransitiveSuperclass env target start =+      case lookupClass start env of+        Nothing -> False+        Just info ->+          target `elem` classSupers info ||+          any (isTransitiveSuperclass env target) (classSupers info)++-- | Find a path through the superclass hierarchy from startClass to targetClass.+-- Returns the list of intermediate classes (excluding startClass, including targetClass).+-- Uses BFS to find the shortest path.+findSuperclassPath :: ClassEnv -> String -> String -> Maybe [String]+findSuperclassPath classEnv startClass targetClass+  | startClass == targetClass = Just []+  | otherwise = bfs [(startClass, [])]+  where+    bfs [] = Nothing+    bfs ((current, path):queue)+      | current == targetClass = Just path+      | otherwise =+          case lookupClass current classEnv of+            Nothing -> bfs queue+            Just info ->+              let nexts = [(s, path ++ [s]) | s <- classSupers info,+                           s `notElem` map fst queue, s `notElem` map fst [(current, path)]]+              in bfs (queue ++ nexts)++-- | Build a chain of superclass dictionary accesses as a TIExprNode.+-- Given dict_Field and path ["Ring", "MulMonoid", "MulSemigroup"],+-- generates: (dict_Field)_("__super_Ring")_("__super_MulMonoid")_("__super_MulSemigroup")+buildSuperclassChain :: TIExpr -> [String] -> TIExpr+buildSuperclassChain dictExpr [] = dictExpr+buildSuperclassChain dictExpr (step:rest) =+  let indexExpr = TIExpr (Forall [] [] TString)+                         (TIConstantExpr (StringExpr (pack ("__super_" ++ step))))+      accessed = TIExpr (Forall [] [] (THash TString TAny))+                        (TIIndexedExpr False dictExpr [Sub indexExpr])+  in buildSuperclassChain accessed rest++-- | Get method type from ClassEnv+-- This retrieves the method type from the class definition and substitutes type variables+-- Note: methodKey is the sanitized name (e.g., "plus"), but classMethods uses original names (e.g., "+")+-- We need to try both the sanitized and original names+getMethodTypeFromClass :: ClassEnv -> String -> String -> Type -> Type+getMethodTypeFromClass classEnv className methodKey constraintType =+  case lookupClass className classEnv of+    Just classInfo ->+      -- Try to find the method by sanitized name first, then try unsanitizing+      case lookup methodKey (classMethods classInfo) `mplus` lookupUnsanitized methodKey (classMethods classInfo) of+        Just classMethodType ->+          -- Substitute class type parameter with actual constraint type+          -- e.g., class Num a has plus : a -> a -> a+          --       constraint Num t0 → plus : t0 -> t0 -> t0+          applySubstsToType [(classParam classInfo, constraintType)] classMethodType+        Nothing -> TAny  -- Method not found in class+    Nothing -> TAny  -- Class not found+  where+    -- Lookup by unsanitizing the method key (reverse of sanitizeMethodName)+    -- e.g., "plus" -> "+", "times" -> "*"+    lookupUnsanitized :: String -> [(String, a)] -> Maybe a+    lookupUnsanitized key methods =+      case unsanitizeMethodName key of+        Just originalName -> lookup originalName methods+        Nothing -> Nothing++    -- Reverse of sanitizeMethodName+    unsanitizeMethodName :: String -> Maybe String+    unsanitizeMethodName "eq" = Just "=="+    unsanitizeMethodName "neq" = Just "/="+    unsanitizeMethodName "lt" = Just "<"+    unsanitizeMethodName "le" = Just "<="+    unsanitizeMethodName "gt" = Just ">"+    unsanitizeMethodName "ge" = Just ">="+    unsanitizeMethodName "plus" = Just "+"+    unsanitizeMethodName "minus" = Just "-"+    unsanitizeMethodName "times" = Just "*"+    unsanitizeMethodName "div" = Just "/"+    unsanitizeMethodName _ = Nothing++-- | Add dictionary parameters to a function based on its type scheme constraints+-- This transforms constrained functions into dictionary-passing style+addDictionaryParametersT :: TypeScheme -> TIExpr -> EvalM TIExpr+addDictionaryParametersT (Forall _vars constraints _ty) tiExpr+  | null constraints = return tiExpr  -- No constraints, no change+  | otherwise = do+      classEnv <- getClassEnv+      -- De-expand constraints to get the minimal set (e.g., {Field a} instead of+      -- {Field a, Ring a, AddGroup a, ...}).  Each root constraint gets one dict param.+      let cs = deExpandConstraints classEnv constraints+      if null cs then return tiExpr+                 else addDictParamsToTIExpr classEnv cs tiExpr+  where+    -- Add dictionary parameters to a TIExpr+    addDictParamsToTIExpr :: ClassEnv -> [Constraint] -> TIExpr -> EvalM TIExpr+    addDictParamsToTIExpr env cs expr = case tiExprNode expr of+      -- Lambda: add dictionary parameters before regular parameters+      TILambdaExpr mVar params body -> do+        let dictParams = map constraintToDictParam cs+            dictVars = map stringToVar dictParams+        -- Remap old expanded dict references (e.g., dict_MulSemigroup) in the body+        -- to superclass chain accesses from the new de-expanded params (e.g.,+        -- dict_Field -> __super_Ring -> __super_MulMonoid -> __super_MulSemigroup).+        -- expandTypeClassMethodsT (Step 1) already resolved methods using expanded+        -- dict names.  Now we remap those to chain accesses from minimal params.+        body' <- case tiExprNode body of+                   TIHashExpr _ -> return body+                   _ -> return $ remapDictRefsInBody env cs body+        let newNode = TILambdaExpr mVar (dictVars ++ params) body'+        return $ TIExpr (tiScheme expr) newNode+      +      -- Hash (dictionary definition): wrap in lambda AND apply dict params to methods+      -- Dictionary values are method references that need dictionary parameters+      TIHashExpr pairs -> do+        let dictParams = map constraintToDictParam cs+            dictVars = map stringToVar dictParams+            wrapperType = tiExprType expr+        +        -- For each value in the hash (which is a method reference),+        -- if it has constraints, apply dictionary parameters to it+        pairs' <- mapM (\(k, v) -> do+          -- Check if the value (method) has constraints+          typeEnv <- getTypeEnv+          let vNode = tiExprNode v+          case vNode of+            TIVarExpr methodName -> do+              case lookupEnv (stringToVar methodName) typeEnv of+                Just (Forall _ vConstraints _) | not (null vConstraints) -> do+                  -- Method has constraints, apply dictionary parameters+                  let dictArgExprs = map (\p -> TIExpr (Forall [] [] (TVar (TyVar "dict"))) (TIVarExpr p)) dictParams+                      vApplied = TIExpr (tiScheme v) (TIApplyExpr v dictArgExprs)+                  return (k, vApplied)+                _ -> return (k, v)  -- No constraints, keep as-is+            _ -> return (k, v)  -- Not a variable, keep as-is+          ) pairs+        +        let hashExpr' = TIExpr (tiScheme expr) (TIHashExpr pairs')+            newNode = TILambdaExpr Nothing dictVars hashExpr'+            newScheme = Forall [] [] wrapperType+        return $ TIExpr newScheme newNode+      +      -- Not a lambda: wrap in a lambda with dictionary parameters+      _ -> do+        let dictParams = map constraintToDictParam cs+            dictVars = map stringToVar dictParams+        -- Special handling for TIVarExpr: if it's a constrained variable, apply dictionaries+        expr' <- case tiExprNode expr of+          TIVarExpr varName -> do+            -- Check if this variable has constraints that match our constraints+            typeEnv <- getTypeEnv+            case lookupEnv (stringToVar varName) typeEnv of+              Just (Forall _ varConstraints _) | not (null varConstraints) -> do+                -- Check which constraints from varConstraints match parent constraints cs+                let (Forall _ exprConstraints _) = tiScheme expr+                    matchingConstraints = filter (\(Constraint eName eType) ->+                          any (\(Constraint pName pType) ->+                            eName == pName && eType == pType) cs) exprConstraints+                if null matchingConstraints+                  then replaceMethodCallsWithDictAccessT env cs expr+                  else do+                    -- Apply matching dictionary parameters+                    let dictArgExprs = map (\p -> TIExpr (Forall [] [] (TVar (TyVar "dict"))) (TIVarExpr p))+                                           (map constraintToDictParam matchingConstraints)+                        varExpr = TIExpr (tiScheme expr) (TIVarExpr varName)+                    return $ TIExpr (tiScheme expr) (TIApplyExpr varExpr dictArgExprs)+              _ -> replaceMethodCallsWithDictAccessT env cs expr+          _ -> replaceMethodCallsWithDictAccessT env cs expr+        let wrapperType = tiExprType expr+            newNode = TILambdaExpr Nothing dictVars expr'+            newScheme = Forall [] [] wrapperType+        return $ TIExpr newScheme newNode+    +    -- Remap old expanded dict references to superclass chain accesses.+    -- When expandTypeClassMethodsT resolved method * with {MulSemigroup a},+    -- it generated dict_MulSemigroup.  After de-expanding to {Field a},+    -- dict_MulSemigroup must become (dict_Field)_("__super_Ring")_(...).+    remapDictRefsInBody :: ClassEnv -> [Constraint] -> TIExpr -> TIExpr+    remapDictRefsInBody env cs tiExpr =+      let s = tiScheme tiExpr+          node' = remapNode (tiExprNode tiExpr)+      in TIExpr s node'+      where+        remapNode :: TIExprNode -> TIExprNode+        remapNode node = case node of+          TIVarExpr name+            | "dict_" `isPrefixOf` name ->+                let oldClass = drop 5 name  -- "dict_Foo" → "Foo"+                in case findDictChain env cs oldClass of+                     Just chainExpr -> tiExprNode chainExpr+                     Nothing -> node+          _ -> mapTIExprChildren (remapDictRefsInBody env cs) node++        findDictChain :: ClassEnv -> [Constraint] -> String -> Maybe TIExpr+        findDictChain classEnv minCs targetClass =+          -- For each de-expanded constraint, try to find a path to targetClass+          let tryConstraint (Constraint cn _ty) =+                case findSuperclassPath classEnv cn targetClass of+                  Just path ->+                    let dictExpr = TIExpr (Forall [] [] (THash TString TAny))+                                         (TIVarExpr ("dict_" ++ cn))+                    in Just (buildSuperclassChain dictExpr path)+                  Nothing -> Nothing+          in case mapMaybe tryConstraint minCs of+               (result:_) -> Just result+               [] -> Nothing++    -- Replace method calls with dictionary access in TIExpr+    replaceMethodCallsWithDictAccessT :: ClassEnv -> [Constraint] -> TIExpr -> EvalM TIExpr+    replaceMethodCallsWithDictAccessT env cs tiExpr = do+      let scheme@(Forall _ exprConstraints exprType) = tiScheme tiExpr+      newNode <- replaceMethodCallsInNode env cs exprConstraints exprType (tiExprNode tiExpr)+      return $ TIExpr scheme newNode+    +    -- Replace method calls in TIExprNode+    replaceMethodCallsInNode :: ClassEnv -> [Constraint] -> [Constraint] -> Type -> TIExprNode -> EvalM TIExprNode+    replaceMethodCallsInNode env cs _exprConstraints exprType node = case node of+      -- Standalone method reference: eta-expand with superclass chain access+      TIVarExpr methodName -> do+        case findConstraintForMethodWithPath env methodName cs of+          Just (constraint, ownerClass, path) -> do+            let tyArg = constraintType constraint+                dictParam = constraintToDictParam constraint+                arity = getMethodArity exprType+                paramTypes = getParamTypes exprType+                paramNames = ["etaVar" ++ show i | i <- [1..arity]]+                paramVars = map stringToVar paramNames+                paramExprs = zipWith (\n t -> TIExpr (Forall [] [] t) (TIVarExpr n)) paramNames paramTypes+                dictExpr = TIExpr (Forall [] [] (THash TString TAny)) (TIVarExpr dictParam)+                chainedDict = buildSuperclassChain dictExpr path+                methodType = getMethodTypeFromClass env ownerClass (sanitizeMethodName methodName) tyArg+                methodScheme = Forall [] [] methodType+                indexExpr = TIExpr (Forall [] [] TString)+                                  (TIConstantExpr (StringExpr (pack (sanitizeMethodName methodName))))+                dictAccess = TIExpr methodScheme $+                             TIIndexedExpr False chainedDict [Sub indexExpr]+            if null paramVars+              then return $ TIIndexedExpr False chainedDict [Sub indexExpr]+              else do+                let body = TIExpr methodScheme (TIApplyExpr dictAccess paramExprs)+                return $ TILambdaExpr Nothing paramVars body+          Nothing ->+            return $ TIVarExpr methodName+      +      -- Method call: replace with dictionary access via superclass chain+      TIApplyExpr func args -> do+        case tiExprNode func of+          TIVarExpr methodName -> do+            case findConstraintForMethodWithPath env methodName cs of+              Just (constraint, ownerClass, path) -> do+                let dictParam = constraintToDictParam constraint+                    tyArg = constraintType constraint+                    dictExpr = TIExpr (Forall [] [] (THash TString TAny)) (TIVarExpr dictParam)+                    chainedDict = buildSuperclassChain dictExpr path+                    methodType = getMethodTypeFromClass env ownerClass (sanitizeMethodName methodName) tyArg+                    methodScheme = Forall [] [] methodType+                    indexExpr = TIExpr (Forall [] [] TString) +                                      (TIConstantExpr (StringExpr (pack (sanitizeMethodName methodName))))+                    dictAccess = TIExpr methodScheme $+                                 TIIndexedExpr False chainedDict [Sub indexExpr]+                args' <- mapM (replaceMethodCallsWithDictAccessT env cs) args+                return $ TIApplyExpr dictAccess args'+              Nothing -> do+                -- Not a method, process recursively+                func' <- replaceMethodCallsWithDictAccessT env cs func+                args' <- mapM (replaceMethodCallsWithDictAccessT env cs) args+                return $ TIApplyExpr func' args'+          _ -> do+            -- Not a simple variable, process recursively+            func' <- replaceMethodCallsWithDictAccessT env cs func+            args' <- mapM (replaceMethodCallsWithDictAccessT env cs) args+            return $ TIApplyExpr func' args'+      +      -- Lambda: recursively process body+      TILambdaExpr mVar params body -> do+        body' <- replaceMethodCallsWithDictAccessT env cs body+        return $ TILambdaExpr mVar params body'+      +      -- If: recursively process+      TIIfExpr cond thenExpr elseExpr -> do+        cond' <- replaceMethodCallsWithDictAccessT env cs cond+        thenExpr' <- replaceMethodCallsWithDictAccessT env cs thenExpr+        elseExpr' <- replaceMethodCallsWithDictAccessT env cs elseExpr+        return $ TIIfExpr cond' thenExpr' elseExpr'+      +      -- Let: recursively process+      TILetExpr bindings body -> do+        bindings' <- mapM (\(pat, e) -> do+          e' <- replaceMethodCallsWithDictAccessT env cs e+          return (pat, e')) bindings+        body' <- replaceMethodCallsWithDictAccessT env cs body+        return $ TILetExpr bindings' body'+      +      -- LetRec: recursively process+      TILetRecExpr bindings body -> do+        bindings' <- mapM (\(pat, e) -> do+          e' <- replaceMethodCallsWithDictAccessT env cs e+          return (pat, e')) bindings+        body' <- replaceMethodCallsWithDictAccessT env cs body+        return $ TILetRecExpr bindings' body'+      +      -- Hash: do NOT process values inside dictionary hashes+      -- Dictionary values should remain as simple references+      -- e.g., {| ("eq", eqCollectionEq), ... |} not {| ("eq", eqCollectionEq dict_Eq), ... |}+      -- We return the node as-is without recursively processing the pairs+      TIHashExpr pairs -> do+        -- Process only keys, not values (values should remain as method references)+        pairs' <- mapM (\(k, v) -> do+          k' <- replaceMethodCallsWithDictAccessT env cs k+          -- Do NOT process v - keep it as a simple reference+          return (k', v)) pairs+        return $ TIHashExpr pairs'+      +      -- Matcher: recursively process expressions inside matcher definitions+      TIMatcherExpr patDefs -> do+        patDefs' <- mapM (\(pat, matcherExpr, bindings) -> do+          -- Process the next-matcher expression+          matcherExpr' <- replaceMethodCallsWithDictAccessT env cs matcherExpr+          -- Process expressions in primitive-data-match clauses+          bindings' <- mapM (\(dp, expr) -> do+            expr' <- replaceMethodCallsWithDictAccessT env cs expr+            return (dp, expr')) bindings+          return (pat, matcherExpr', bindings')) patDefs+        return $ TIMatcherExpr patDefs'+      +      -- Other expressions: return as-is for now+      _ -> return node++-- | Apply dictionaries to expressions with concrete type constraints+-- This is used for top-level definitions like: def integer : Matcher Integer := eq+-- where the right-hand side (eq) has concrete type constraints {Eq Integer}+applyConcreteConstraintDictionaries :: TIExpr -> EvalM TIExpr+applyConcreteConstraintDictionaries expr = do+  classEnv <- getClassEnv+  let scheme@(Forall vars constraints _) = tiScheme expr++  -- First, recursively process sub-expressions+  expr' <- case tiExprNode expr of+    TIApplyExpr func args -> do+      func' <- applyConcreteConstraintDictionaries func+      args' <- mapM applyConcreteConstraintDictionaries args+      return $ TIExpr scheme (TIApplyExpr func' args')+    -- Indexed access (`R'_i_j_k~l` etc.) wraps a base expression that may+    -- itself need dict args applied. Without this recursion, a tensor-valued+    -- def whose body uses typeclass methods (e.g. `def R'_i_j_k~l :=+    -- generateTensor (\... -> Ring ops ...)`) keeps its `\dict_AddGroup ...+    -- ->` outer wrapper at the use-site, and the IIndexedExpr eval path+    -- (Core.hs `Value (Func ...)` arm) treats the lambda directly as a+    -- function-with-index, raising "Expected number, but found: <lambda>"+    -- when the lambda is later used in arithmetic.+    TIIndexedExpr override base indices -> do+      base' <- applyConcreteConstraintDictionaries base+      indices' <- mapM (traverse applyConcreteConstraintDictionaries) indices+      return $ TIExpr scheme (TIIndexedExpr override base' indices')+    _ -> return expr++  -- De-expand constraints to minimal set, then check if concrete+  let minConstraints = deExpandConstraints classEnv constraints+      isConcreteConstraint c = all isConcreteConstraintType (constraintTypes c)+      isConcreteConstraintType (TVar _) = False+      isConcreteConstraintType _        = True+      hasOnlyConcreteConstraints = not (null minConstraints) && all isConcreteConstraint minConstraints++  if hasOnlyConcreteConstraints+    then do+      dictArgs <- mapM (resolveDictionaryForConstraint classEnv) minConstraints+      let resultType = tiExprType expr'+          newScheme = Forall vars [] resultType+      -- Insert dict args between function and regular args+      case tiExprNode expr' of+        TIApplyExpr func args ->+          let funcWithDicts = TIExpr (tiScheme func) (TIApplyExpr func dictArgs)+          in return $ TIExpr newScheme (TIApplyExpr funcWithDicts args)+        _ ->+          return $ TIExpr newScheme (TIApplyExpr expr' dictArgs)+    else+      return expr'+  where+    -- Resolve dictionary for a concrete constraint.+    -- Multi-param classes use full-list dispatch; single-param falls back.+    resolveDictionaryForConstraint :: ClassEnv -> Constraint -> EvalM TIExpr+    resolveDictionaryForConstraint classEnv (Constraint className tyArgs) = do+      -- Normalize TInt to TMathValue for instance matching+      -- (Integer and MathValue share runtime representation in Egison).+      let normalizeType t = case t of+                              TInt -> TMathValue+                              _    -> t+          normalizedTypes = map normalizeType tyArgs+          instances = lookupInstances className classEnv+      mInst <- findInstanceForDispatch normalizedTypes instances+      case mInst of+        Just inst -> do+          -- Generate dictionary name (e.g., "eqInteger", "numInteger")+          let instTypeName = concatMap typeToName (instTypes inst)+              dictName = lowerFirst className ++ instTypeName+              dictType = TVar (TyVar "dict")+              dictExpr = TIExpr (Forall [] [] dictType) (TIVarExpr dictName)+          +          -- Check if instance has nested constraints+          if null (instContext inst)+            then do+              -- No constraints: return simple dictionary reference+              return dictExpr+            else do+              -- Has constraints: need to resolve them recursively+              nestedDictArgs <- mapM (resolveDictionaryForConstraint classEnv) (instContext inst)+              return $ TIExpr (Forall [] [] dictType) (TIApplyExpr dictExpr nestedDictArgs)+        Nothing -> do+          -- No instance found - return dummy dictionary+          let dictName = "dict_" ++ className ++ "_NOT_FOUND"+              dictType = TVar (TyVar "dict")+          return $ TIExpr (Forall [] [] dictType) (TIVarExpr dictName)++-- | Expand type class method calls in patterns+-- This is a public wrapper for expandTIPattern used by TypedDesugar+expandTypeClassMethodsInPattern :: TIPattern -> EvalM TIPattern+expandTypeClassMethodsInPattern tipat = do+  classEnv <- getClassEnv+  expandPatternWithClassEnv classEnv tipat+  where+    expandPatternWithClassEnv :: ClassEnv -> TIPattern -> EvalM TIPattern+    expandPatternWithClassEnv classEnv' (TIPattern scheme node) = do+      node' <- expandPatternNode classEnv' node+      return $ TIPattern scheme node'+    +    expandPatternNode :: ClassEnv -> TIPatternNode -> EvalM TIPatternNode+    expandPatternNode classEnv' node = case node of+      TILoopPat var loopRange pat1 pat2 -> do+        loopRange' <- expandLoopRange classEnv' loopRange+        pat1' <- expandPatternWithClassEnv classEnv' pat1+        pat2' <- expandPatternWithClassEnv classEnv' pat2+        return $ TILoopPat var loopRange' pat1' pat2'+      +      TIAndPat pat1 pat2 -> do+        pat1' <- expandPatternWithClassEnv classEnv' pat1+        pat2' <- expandPatternWithClassEnv classEnv' pat2+        return $ TIAndPat pat1' pat2'+      +      TIOrPat pat1 pat2 -> do+        pat1' <- expandPatternWithClassEnv classEnv' pat1+        pat2' <- expandPatternWithClassEnv classEnv' pat2+        return $ TIOrPat pat1' pat2'+      +      TIForallPat pat1 pat2 -> do+        pat1' <- expandPatternWithClassEnv classEnv' pat1+        pat2' <- expandPatternWithClassEnv classEnv' pat2+        return $ TIForallPat pat1' pat2'+      +      TINotPat pat -> do+        pat' <- expandPatternWithClassEnv classEnv' pat+        return $ TINotPat pat'+      +      TITuplePat pats -> do+        pats' <- mapM (expandPatternWithClassEnv classEnv') pats+        return $ TITuplePat pats'+      +      TIInductivePat name pats -> do+        pats' <- mapM (expandPatternWithClassEnv classEnv') pats+        return $ TIInductivePat name pats'+      +      TIIndexedPat pat exprs -> do+        pat' <- expandPatternWithClassEnv classEnv' pat+        exprs' <- mapM expandTypeClassMethodsT exprs+        return $ TIIndexedPat pat' exprs'+      +      TILetPat bindings pat -> do+        pat' <- expandPatternWithClassEnv classEnv' pat+        bindings' <- mapM (\(pd, e) -> do+          e' <- expandTypeClassMethodsT e+          return (pd, e')) bindings+        return $ TILetPat bindings' pat'+      +      TIPApplyPat funcExpr argPats -> do+        funcExpr' <- expandTypeClassMethodsT funcExpr+        argPats' <- mapM (expandPatternWithClassEnv classEnv') argPats+        return $ TIPApplyPat funcExpr' argPats'+      +      TIDApplyPat pat pats -> do+        pat' <- expandPatternWithClassEnv classEnv' pat+        pats' <- mapM (expandPatternWithClassEnv classEnv') pats+        return $ TIDApplyPat pat' pats'+      +      TISeqConsPat pat1 pat2 -> do+        pat1' <- expandPatternWithClassEnv classEnv' pat1+        pat2' <- expandPatternWithClassEnv classEnv' pat2+        return $ TISeqConsPat pat1' pat2'+      +      TIInductiveOrPApplyPat name pats -> do+        pats' <- mapM (expandPatternWithClassEnv classEnv') pats+        return $ TIInductiveOrPApplyPat name pats'+      +      TIValuePat expr -> do+        expr' <- expandTypeClassMethodsT expr+        expr'' <- applyConcreteConstraintDictionaries expr'+        return $ TIValuePat expr''+      +      TIPredPat pred -> do+        pred' <- expandTypeClassMethodsT pred+        pred'' <- applyConcreteConstraintDictionaries pred'+        return $ TIPredPat pred''+      +      -- Leaf patterns+      TISeqNilPat -> return TISeqNilPat+      TIVarPat name -> return $ TIVarPat name+      TIWildCard -> return TIWildCard+      TIPatVar name -> return $ TIPatVar name+      TIContPat -> return TIContPat+      TILaterPatVar -> return TILaterPatVar+    +    expandLoopRange :: ClassEnv -> TILoopRange -> EvalM TILoopRange+    expandLoopRange classEnv' (TILoopRange start end rangePat) = do+      start' <- expandTypeClassMethodsT start+      end' <- expandTypeClassMethodsT end+      rangePat' <- expandPatternWithClassEnv classEnv' rangePat+      return $ TILoopRange start' end' rangePat'++-- | Apply dictionaries to expressions with concrete constraints in patterns+-- This is used to apply dictionaries to value patterns like #(n + 1)+applyConcreteConstraintDictionariesInPattern :: TIPattern -> EvalM TIPattern+applyConcreteConstraintDictionariesInPattern (TIPattern scheme node) = do+  node' <- applyDictInPatternNode node+  return $ TIPattern scheme node'+  where+    applyDictInPatternNode :: TIPatternNode -> EvalM TIPatternNode+    applyDictInPatternNode pnode = case pnode of+      TIValuePat expr -> do+        expr' <- applyConcreteConstraintDictionaries expr+        return $ TIValuePat expr'+      +      TIPredPat expr -> do+        expr' <- applyConcreteConstraintDictionaries expr+        return $ TIPredPat expr'+      +      TIIndexedPat pat exprs -> do+        pat' <- applyConcreteConstraintDictionariesInPattern pat+        exprs' <- mapM applyConcreteConstraintDictionaries exprs+        return $ TIIndexedPat pat' exprs'+      +      TILetPat bindings pat -> do+        pat' <- applyConcreteConstraintDictionariesInPattern pat+        bindings' <- mapM (\(pd, e) -> do+          e' <- applyConcreteConstraintDictionaries e+          return (pd, e')) bindings+        return $ TILetPat bindings' pat'+      +      TILoopPat var loopRange pat1 pat2 -> do+        loopRange' <- applyDictInLoopRange loopRange+        pat1' <- applyConcreteConstraintDictionariesInPattern pat1+        pat2' <- applyConcreteConstraintDictionariesInPattern pat2+        return $ TILoopPat var loopRange' pat1' pat2'+      +      TIAndPat pat1 pat2 -> do+        pat1' <- applyConcreteConstraintDictionariesInPattern pat1+        pat2' <- applyConcreteConstraintDictionariesInPattern pat2+        return $ TIAndPat pat1' pat2'+      +      TIOrPat pat1 pat2 -> do+        pat1' <- applyConcreteConstraintDictionariesInPattern pat1+        pat2' <- applyConcreteConstraintDictionariesInPattern pat2+        return $ TIOrPat pat1' pat2'+      +      TIForallPat pat1 pat2 -> do+        pat1' <- applyConcreteConstraintDictionariesInPattern pat1+        pat2' <- applyConcreteConstraintDictionariesInPattern pat2+        return $ TIForallPat pat1' pat2'+      +      TINotPat pat -> do+        pat' <- applyConcreteConstraintDictionariesInPattern pat+        return $ TINotPat pat'+      +      TITuplePat pats -> do+        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats+        return $ TITuplePat pats'+      +      TIInductivePat name pats -> do+        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats+        return $ TIInductivePat name pats'+      +      TIPApplyPat funcExpr argPats -> do+        funcExpr' <- applyConcreteConstraintDictionaries funcExpr+        argPats' <- mapM applyConcreteConstraintDictionariesInPattern argPats+        return $ TIPApplyPat funcExpr' argPats'+      +      TIDApplyPat pat pats -> do+        pat' <- applyConcreteConstraintDictionariesInPattern pat+        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats+        return $ TIDApplyPat pat' pats'+      +      TISeqConsPat pat1 pat2 -> do+        pat1' <- applyConcreteConstraintDictionariesInPattern pat1+        pat2' <- applyConcreteConstraintDictionariesInPattern pat2+        return $ TISeqConsPat pat1' pat2'+      +      TIInductiveOrPApplyPat name pats -> do+        pats' <- mapM applyConcreteConstraintDictionariesInPattern pats+        return $ TIInductiveOrPApplyPat name pats'+      +      -- Leaf patterns+      TISeqNilPat -> return TISeqNilPat+      TIVarPat name -> return $ TIVarPat name+      TIWildCard -> return TIWildCard+      TIPatVar name -> return $ TIPatVar name+      TIContPat -> return TIContPat+      TILaterPatVar -> return TILaterPatVar+    +    applyDictInLoopRange :: TILoopRange -> EvalM TILoopRange+    applyDictInLoopRange (TILoopRange start end rangePat) = do+      start' <- applyConcreteConstraintDictionaries start+      end' <- applyConcreteConstraintDictionaries end+      rangePat' <- applyConcreteConstraintDictionariesInPattern rangePat+      return $ TILoopRange start' end' rangePat'++-- ============================================================================+-- Unbound dictionary reference repair (post-pass)+-- ============================================================================++-- | Rewrite dictionary accesses whose dictionary PARAMETER is not lambda-bound+-- into 'TIRuntimeDispatch'.+--+-- The expansion above rewrites a class-method use whose constraint is still a+-- type variable into an access on the dictionary parameter @dict_<Class>@,+-- assuming the enclosing definition's scheme carries the constraint so that+-- 'addDictionaryParametersT' binds that parameter.  When constraint+-- information was lost (e.g. a definition whose signature is missing a+-- {Class a} it actually needs, or inference corners that drop node+-- substitutions), the reference stays UNBOUND: at runtime the variable+-- silently evaluates to a symbol, the string index (the sanitized method+-- name, e.g. "ge" for >=) ends up inside CAS data, and evaluation fails far+-- away with @Expected CASData, but found: "ge"@.+--+-- This pass runs AFTER 'addDictionaryParametersT' (so legitimate dictionary+-- parameters are visible as lambda binders) and rewrites any remaining+-- access through an unbound @dict_*@ root into a runtime-type dispatch on+-- the method's owner class — the same mechanism used for MathValue-like+-- dispatch — which resolves the instance from the first argument's runtime+-- type.  Accesses with no arguments (0-arity methods like @zero@) cannot be+-- dispatched at runtime and are left unchanged.+fixUnboundDictRefs :: ClassEnv -> TIExpr -> TIExpr+fixUnboundDictRefs classEnv = goE Set.empty+  where+    goE :: Set.Set String -> TIExpr -> TIExpr+    goE scope (TIExpr sch node) = TIExpr sch (goN scope node)++    goN :: Set.Set String -> TIExprNode -> TIExprNode+    goN scope node = case node of+      -- Lambda binders may introduce dictionary parameters (from+      -- addDictionaryParametersT or instance-dictionary functions).+      TILambdaExpr mVar params body ->+        let scope' = foldr Set.insert scope+                       [ n | Var n _ <- params, "dict_" `isPrefixOf` n ]+        in TILambdaExpr mVar params (goE scope' body)+      TIMemoizedLambdaExpr params body ->+        let scope' = foldr Set.insert scope+                       [ n | n <- params, "dict_" `isPrefixOf` n ]+        in TIMemoizedLambdaExpr params (goE scope' body)++      -- A method application through an unbound dictionary parameter:+      -- (dict_C[__super_X]...["m"]) args  ==>  runtime dispatch on the owner.+      TIApplyExpr fn args+        | Just (root, owner, methodKey) <- splitDictAccess fn+        , not (root `Set.member` scope)+        , candidates <- runtimeDispatchCandidates (lookupInstances owner classEnv)+        , not (null candidates)+        -> TIRuntimeDispatch owner methodKey candidates (map (goE scope) args)++      -- An unbound dictionary parameter used as a VALUE — typically passed+      -- as a dictionary ARGUMENT to a constrained function, e.g.+      -- `(sort dict_Ord) xs` inside a definition whose signature lacks+      -- {Ord a}.  Synthesize a dictionary whose every method dispatches on+      -- its first argument's runtime type, so the callee's dictionary+      -- accesses keep working.  (0-arity methods cannot be dispatched at+      -- runtime and are omitted; superclass entries are synthesized+      -- recursively.)+      TIVarExpr d+        | "dict_" `isPrefixOf` d+        , not (d `Set.member` scope)+        , let cls = drop (length ("dict_" :: String)) d+        , Just _ <- lookupClass cls classEnv+        -> tiExprNode (dispatchDictFor 3 cls)++      -- Patterns embed expressions (value patterns, predicate patterns,+      -- pattern-function applications, loop ranges); mapTIExprChildren does+      -- not descend into them, so handle the pattern-carrying nodes here.+      TIMatchExpr mode tgt mat clauses ->+        TIMatchExpr mode (goE scope tgt) (goE scope mat)+                    [ (goP scope p, goE scope b) | (p, b) <- clauses ]+      TIMatchAllExpr mode tgt mat clauses ->+        TIMatchAllExpr mode (goE scope tgt) (goE scope mat)+                       [ (goP scope p, goE scope b) | (p, b) <- clauses ]++      _ -> mapTIExprChildren (goE scope) node++    goP :: Set.Set String -> TIPattern -> TIPattern+    goP scope (TIPattern sch pnode) = TIPattern sch (goPN scope pnode)++    goPN :: Set.Set String -> TIPatternNode -> TIPatternNode+    goPN scope pnode = case pnode of+      TIValuePat e             -> TIValuePat (goE scope e)+      TIPredPat e              -> TIPredPat (goE scope e)+      TIIndexedPat p es        -> TIIndexedPat (goP scope p) (map (goE scope) es)+      TILetPat bs p            -> TILetPat [ (dp, goE scope e) | (dp, e) <- bs ] (goP scope p)+      TINotPat p               -> TINotPat (goP scope p)+      TIAndPat p1 p2           -> TIAndPat (goP scope p1) (goP scope p2)+      TIOrPat p1 p2            -> TIOrPat (goP scope p1) (goP scope p2)+      TIForallPat p1 p2        -> TIForallPat (goP scope p1) (goP scope p2)+      TITuplePat ps            -> TITuplePat (map (goP scope) ps)+      TIInductivePat n ps      -> TIInductivePat n (map (goP scope) ps)+      TILoopPat v (TILoopRange s e rp) p1 p2 ->+        TILoopPat v (TILoopRange (goE scope s) (goE scope e) (goP scope rp))+                  (goP scope p1) (goP scope p2)+      TIPApplyPat e ps         -> TIPApplyPat (goE scope e) (map (goP scope) ps)+      TIInductiveOrPApplyPat n ps -> TIInductiveOrPApplyPat n (map (goP scope) ps)+      TISeqConsPat p1 p2       -> TISeqConsPat (goP scope p1) (goP scope p2)+      TIDApplyPat p ps         -> TIDApplyPat (goP scope p) (map (goP scope) ps)+      TIWildCard               -> pnode+      TIPatVar _               -> pnode+      TIVarPat _               -> pnode+      TIContPat                -> pnode+      TISeqNilPat              -> pnode+      TILaterPatVar            -> pnode++    -- Recognize a dictionary access chain rooted at a dict_* variable:+    --   dict_C ["m"]                       -> (dict_C, C, m)+    --   dict_C ["__super_X"]... ["m"]      -> (dict_C, X-of-last-hop, m)+    splitDictAccess :: TIExpr -> Maybe (String, String, String)+    splitDictAccess (TIExpr _ (TIIndexedExpr _ base [Sub keyExpr])) = do+      key <- stringKeyOf keyExpr+      if "__super_" `isPrefixOf` key+        then Nothing  -- a bare superclass hop is not a method access+        else do+          (root, hops) <- dictRootOf base+          ownerFromChain root hops key+    splitDictAccess _ = Nothing++    ownerFromChain :: String -> [String] -> String -> Maybe (String, String, String)+    ownerFromChain root hops key =+      let owner = case hops of+                    [] -> drop (length ("dict_" :: String)) root+                    _  -> drop (length ("__super_" :: String)) (last hops)+      in if null owner then Nothing else Just (root, owner, key)++    -- Walk down nested __super_ accesses to the dict_* root.+    -- Returns (root variable name, super hops in outermost-last order).+    dictRootOf :: TIExpr -> Maybe (String, [String])+    dictRootOf (TIExpr _ (TIVarExpr n))+      | "dict_" `isPrefixOf` n = Just (n, [])+    dictRootOf (TIExpr _ (TIIndexedExpr _ base [Sub keyExpr])) = do+      key <- stringKeyOf keyExpr+      if "__super_" `isPrefixOf` key+        then do (root, hops) <- dictRootOf base+                return (root, hops ++ [key])+        else Nothing+    dictRootOf _ = Nothing++    stringKeyOf :: TIExpr -> Maybe String+    stringKeyOf (TIExpr _ (TIConstantExpr (StringExpr t))) = Just (unpack t)+    stringKeyOf _ = Nothing++    -- A dictionary value for class `cls` whose methods are runtime+    -- dispatchers.  Used as the stand-in for an unbound dict_<cls>.+    dispatchDictFor :: Int -> String -> TIExpr+    dispatchDictFor depth cls =+      let (methods, supers) = case lookupClass cls classEnv of+            Just ci -> (classMethods ci, classSupers ci)+            Nothing -> ([], [])+          candidates = runtimeDispatchCandidates (lookupInstances cls classEnv)+          anyScheme = Forall [] [] TAny+          strScheme = Forall [] [] TString+          strKey k = TIExpr strScheme (TIConstantExpr (StringExpr (pack k)))+          mkMethod (mname, mty) =+            let key = sanitizeMethodName mname+                arity = getMethodArity mty+                params = ["dispatchArg" ++ show i | i <- [1 .. arity]]+                argEs = [TIExpr anyScheme (TIVarExpr p) | p <- params]+                body = TIExpr anyScheme (TIRuntimeDispatch cls key candidates argEs)+            in if arity == 0 || null candidates+                 then Nothing+                 else Just (strKey key,+                            TIExpr anyScheme (TILambdaExpr Nothing (map stringToVar params) body))+          superEntries+            | depth <= 0 = []+            | otherwise =+                [ (strKey ("__super_" ++ s), dispatchDictFor (depth - 1) s)+                | s <- supers ]+      in TIExpr (Forall [] [] (THash TString TAny))+                (TIHashExpr (mapMaybe mkMethod methods ++ superEntries))
hs-src/Language/Egison/Type/TypedDesugar.hs view
@@ -2,11 +2,11 @@ Module      : Language.Egison.Type.TypedDesugar Licence     : MIT -This module implements Phase 8 of the processing flow: TypedDesugar.+This module implements Phase 7 of the processing flow: TypedDesugar. It orchestrates type-driven transformations on TIExpr (Typed Internal Expressions) by calling specialized expansion modules. -Type-Driven Transformations (Phase 8):+Type-Driven Transformations (Phase 7):   1. Type class dictionary passing (via TypeClassExpand)      - Instance selection based on types      - Method call concretization@@ -29,11 +29,32 @@  import           Language.Egison.Data       (EvalM) import           Language.Egison.EvalState  (MonadEval(..))-import           Language.Egison.IExpr      (TIExpr(..), TITopExpr(..), extractNameFromVar, stringToVar)+import           Language.Egison.IExpr      (TIExpr(..), TIExprNode(..), TITopExpr(..), extractNameFromVar, stringToVar) import           Language.Egison.Type.Env   (lookupEnv) import           Language.Egison.Type.TensorMapInsertion (insertTensorMaps)-import           Language.Egison.Type.TypeClassExpand (expandTypeClassMethodsT, expandTypeClassMethodsInPattern, addDictionaryParametersT, applyConcreteConstraintDictionaries, applyConcreteConstraintDictionariesInPattern)+import           Language.Egison.Type.Types (Type(..), TypeScheme(..))+import           Language.Egison.Type.TypeClassExpand (expandTypeClassMethodsT, expandTypeClassMethodsInPattern, addDictionaryParametersT, applyConcreteConstraintDictionaries, applyConcreteConstraintDictionariesInPattern, fixUnboundDictRefs) +-- | Wrap a TIExpr with TIReshape when the type scheme demands a concrete+-- CAS scalar type (Integer, Frac _, Poly _ _, Term _ _, Factor). Skip for+-- polymorphic schemes (any type variables or class constraints), non-CAS+-- types, and TMathValue (which is the most general — reshape is a no-op).+-- This is the post-typecheck elaboration step; placing it after type class+-- expansion preserves inner-method dispatch context.+maybeReshape :: TypeScheme -> TIExpr -> TIExpr+maybeReshape sch@(Forall vars constraints ty) tiexpr+  | not (null vars) || not (null constraints) = tiexpr+  | isReshapeTarget ty = TIExpr sch (TIReshape ty tiexpr)+  | otherwise = tiexpr+  where+    isReshapeTarget :: Type -> Bool+    isReshapeTarget TInt        = True+    isReshapeTarget TFactor     = True+    isReshapeTarget (TFrac _)   = True+    isReshapeTarget (TPoly _ _) = True+    isReshapeTarget (TTerm _ _) = True+    isReshapeTarget _           = False+ -- | Desugar a typed expression (TIExpr) with type-driven transformations -- This function orchestrates the transformation pipeline: --   1. Insert tensorMap where needed (TensorMapInsertion)@@ -53,7 +74,7 @@   return tiexpr''  -- | Desugar a top-level typed expression (TITopExpr)--- This is the main entry point for Phase 8 transformations.+-- This is the main entry point for Phase 7 transformations. desugarTypedTopExprT :: TITopExpr -> EvalM (Maybe TITopExpr) desugarTypedTopExprT topExpr = case topExpr of   TIDefine scheme var tiexpr -> do@@ -62,15 +83,22 @@     tiexpr'' <- applyConcreteConstraintDictionaries tiexpr'     -- Add dictionary parameters for constrained functions     tiexpr''' <- addDictionaryParametersT scheme tiexpr''-    return $ Just (TIDefine scheme var tiexpr''')-  +    -- Repair any dictionary access left unbound (fall back to runtime dispatch)+    classEnv <- getClassEnv+    let tiexprFixed = fixUnboundDictRefs classEnv tiexpr'''+    -- Insert TIReshape from type annotation (post-typecheck elaboration)+    let tiexprFinal = maybeReshape scheme tiexprFixed+    return $ Just (TIDefine scheme var tiexprFinal)+   TITest tiexpr -> do     tiexpr' <- desugarTypedExprT tiexpr-    return $ Just (TITest tiexpr')-  +    classEnv <- getClassEnv+    return $ Just (TITest (fixUnboundDictRefs classEnv tiexpr'))+   TIExecute tiexpr -> do     tiexpr' <- desugarTypedExprT tiexpr-    return $ Just (TIExecute tiexpr')+    classEnv <- getClassEnv+    return $ Just (TIExecute (fixUnboundDictRefs classEnv tiexpr'))      TILoadFile path ->      return $ Just (TILoadFile path)@@ -90,7 +118,8 @@                      Just ts -> ts  -- Use type scheme from environment                      Nothing -> tiScheme tiexpr'  -- Fallback to expression's scheme       tiexpr'' <- addDictionaryParametersT scheme tiexpr'-      return (var, tiexpr'')) bindings+      classEnv <- getClassEnv+      return (var, fixUnboundDictRefs classEnv tiexpr'')) bindings     return $ Just (TIDefineMany bindings')      TIDeclareSymbol names ty ->@@ -150,15 +179,24 @@     tiexpr'' <- applyConcreteConstraintDictionaries tiexpr'     -- Add dictionary parameters for constrained functions     tiexpr''' <- addDictionaryParametersT scheme tiexpr''-    return $ Just (TIDefine scheme var tiexpr''')+    -- Repair any dictionary access left unbound (fall back to runtime dispatch)+    classEnv <- getClassEnv+    let tiexprFixed = fixUnboundDictRefs classEnv tiexpr'''+    -- Insert TIReshape from type annotation (post-typecheck elaboration)+    let tiexprFinal = maybeReshape scheme tiexprFixed+    return $ Just (TIDefine scheme var tiexprFinal)    TITest tiexpr -> do     tiexpr' <- expandTypeClassMethodsT tiexpr-    return $ Just (TITest tiexpr')+    tiexpr'' <- applyConcreteConstraintDictionaries tiexpr'+    classEnv <- getClassEnv+    return $ Just (TITest (fixUnboundDictRefs classEnv tiexpr''))    TIExecute tiexpr -> do     tiexpr' <- expandTypeClassMethodsT tiexpr-    return $ Just (TIExecute tiexpr')+    tiexpr'' <- applyConcreteConstraintDictionaries tiexpr'+    classEnv <- getClassEnv+    return $ Just (TIExecute (fixUnboundDictRefs classEnv tiexpr''))    TILoadFile path ->     return $ Just (TILoadFile path)@@ -176,7 +214,8 @@                      Just ts -> ts                      Nothing -> tiScheme tiexpr'       tiexpr'' <- addDictionaryParametersT scheme tiexpr'-      return (var, tiexpr'')) bindings+      classEnv <- getClassEnv+      return (var, fixUnboundDictRefs classEnv tiexpr'')) bindings     return $ Just (TIDefineMany bindings')      TIDeclareSymbol names ty ->
hs-src/Language/Egison/Type/Types.hs view
@@ -5,43 +5,62 @@ This module defines the type system for Egison. -} -{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric        #-}+{-# LANGUAGE DeriveAnyClass       #-}+{-# LANGUAGE DerivingStrategies   #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}  module Language.Egison.Type.Types   ( Type(..)+  , SymbolSet(..)+  , TypeAtom(..)+  , prettyTypeAtomValue   , TypeScheme(..)   , TyVar(..)   , TensorShape(..)   , ShapeDimType(..)   , Constraint(..)+  , constraintType  -- backward-compat: head of constraintTypes   , ClassInfo(..)+  , classParam   , InstanceInfo(..)+  , instType   , freshTyVar   , freeTyVars   , isTensorType   , isScalarType+  , isCASType+  , isSubsetSymbolSet+  , hasAmbiguousOpenTower+  , mapType+  , substTyVar+  , typeAtomExprToTypeAtom   , typeToName   , typeConstructorName   , sanitizeMethodName   , typeExprToType   , normalizeInductiveTypes+  , expandTypeAliases+  , reservedCasTypeNames   , capitalizeFirst   , lowerFirst   ) where  import           Data.Char        (toLower, toUpper)+import           Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as HashMap import           Data.Hashable    (Hashable) import           Data.Set         (Set) import qualified Data.Set         as Set import           GHC.Generics     (Generic) -import           Language.Egison.AST        (TypeExpr(..))+import           Language.Egison.AST        (TypeExpr(..), SymbolSetExpr(..), TypeAtomExpr(..)) import           Language.Egison.Type.Index ()  -- | Type variable newtype TyVar = TyVar String-  deriving (Eq, Ord, Show, Generic, Hashable)+  deriving stock (Eq, Ord, Show, Generic)+  deriving newtype Hashable  -- | Shape dimension (can be concrete or variable) data ShapeDimType@@ -57,14 +76,50 @@   | ShapeUnknown              -- ^ To be inferred   deriving (Eq, Ord, Show, Generic, Hashable) +-- | A type-level atom inside a closed symbol set.+-- Mirrors `TypeAtomExpr` from the AST; conversion is handled by+-- `typeAtomExprToTypeAtom`.+data TypeAtom+  = TANameAtom String             -- ^ A plain identifier, e.g. `x`, `i`+  | TAApplyAtom String [TypeAtom] -- ^ A function applied to atom args, e.g. `sin x`+  | TAIntAtom  Integer            -- ^ An integer literal in atom position+  deriving (Eq, Ord, Show, Generic, Hashable)++-- | Pretty print a TypeAtom in canonical form.+prettyTypeAtomValue :: TypeAtom -> String+prettyTypeAtomValue (TANameAtom s)        = s+prettyTypeAtomValue (TAIntAtom n)         = show n+prettyTypeAtomValue (TAApplyAtom fn args) = unwords (fn : map prettyAtomArg args)+  where+    prettyAtomArg a@(TAApplyAtom _ _) = "(" ++ prettyTypeAtomValue a ++ ")"+    prettyAtomArg a                   = prettyTypeAtomValue a++-- | Convert an AST-level atom to a Type-level atom (1-to-1 correspondence).+typeAtomExprToTypeAtom :: TypeAtomExpr -> TypeAtom+typeAtomExprToTypeAtom (TAEName s)       = TANameAtom s+typeAtomExprToTypeAtom (TAEInt n)        = TAIntAtom n+typeAtomExprToTypeAtom (TAEApp fn args)  = TAApplyAtom fn (map typeAtomExprToTypeAtom args)++-- | Symbol set for polynomial types+-- Used to specify the indeterminates in a polynomial type+data SymbolSet+  = SymbolSetClosed [TypeAtom]  -- ^ Fixed symbol set, e.g., [x, y, sqrt 2]+  | SymbolSetOpen               -- ^ Open symbol set, e.g., [..] in Poly Integer [..]+  | SymbolSetVar TyVar          -- ^ Symbol set variable (for unification with open sets)+  deriving (Eq, Ord, Show, Generic, Hashable)+ -- | Egison types data Type   = TInt                              -- ^ Integer-  | TMathExpr                         -- ^ MathExpr (mathematical expression, unifies with Integer)-  | TPolyExpr                         -- ^ PolyExpr (polynomial expression)-  | TTermExpr                         -- ^ TermExpr (term in polynomial)-  | TSymbolExpr                       -- ^ SymbolExpr (symbolic variable)-  | TIndexExpr                        -- ^ IndexExpr (subscript/superscript index)+  | TMathValue                         -- ^ MathValue (mathematical expression, unifies with Integer)+  -- The four *Expr types below type the views of the internal+  -- mathematical-expression data: primitive data patterns in matcher+  -- definitions (Plus / Term / Symbol / Apply1..4 / Quote / Function, as in+  -- lib/math/expression.egi) are given these types by the inference.+  | TPolyExpr                         -- ^ PolyExpr (a polynomial view: Plus)+  | TTermExpr                         -- ^ TermExpr (a term view: Term)+  | TSymbolExpr                       -- ^ SymbolExpr (a factor view: Symbol, Apply1..4, Quote, Function)+  | TIndexExpr                        -- ^ IndexExpr (an index view: Sub/Sup/User; also a surface type name)   | TFloat                            -- ^ Float (Double)   | TBool                             -- ^ Bool   | TChar                             -- ^ Char@@ -76,44 +131,71 @@   | TTensor Type                      -- ^ Tensor type (only element type is kept). Vector and Matrix are aliases for Tensor   | THash Type Type                   -- ^ Hash map type   | TMatcher Type                     -- ^ Matcher type, e.g., Matcher a+  | TMatcherSlot Type Type            -- ^ Matcher consumer position, e.g., MatcherSlot tau_p tau_t (structural index tau_p / target index tau_t)   | TFun Type Type                    -- ^ Function type, e.g., a -> b   | TIO Type                          -- ^ IO type (for IO actions)   | TIORef Type                       -- ^ IORef type   | TPort                             -- ^ Port type (file handles)   | TAny                              -- ^ Any type (for gradual typing)+  -- New CAS types (Phase 2)+  | TFactor                           -- ^ Factor type (atomic mathematical factor from quote ')+  | TTerm Type SymbolSet               -- ^ Term type, e.g., Term Integer [x] = monomials over Integer with atom set [x]+  | TFrac Type                         -- ^ Frac type, e.g., Frac Integer = rationals+  | TPoly Type SymbolSet              -- ^ Poly type, e.g., Poly Integer [x, y] or Poly Integer [..]   deriving (Eq, Ord, Show, Generic, Hashable) --- | Type alias: MathExpr = Integer in Egison--- Both names refer to the same type (TInt)-tMathExpr :: Type-tMathExpr = TInt- -- | Type scheme for polymorphic types (∀a. C a => Type) -- Includes type constraints for type class support data TypeScheme = Forall [TyVar] [Constraint] Type   deriving (Eq, Show, Generic) --- | Type class constraint, e.g., "Eq a"+-- | Type class constraint. May carry multiple type arguments for+-- multi-param classes (e.g. `Coerce a b` → `Constraint "Coerce" [a, b]`).+-- Single-param classes use a one-element list. data Constraint = Constraint   { constraintClass :: String  -- ^ Class name, e.g., "Eq"-  , constraintType  :: Type    -- ^ Type argument, e.g., TVar "a"+  , constraintTypes :: [Type]  -- ^ Type arguments. For single-param classes,+                                -- a singleton list `[t]`. For multi-param, all+                                -- class type parameters in declaration order.   } deriving (Eq, Show, Generic) +-- | Accessor for the principal (first) constraint type.+-- For single-param classes this is the only type. Multi-param call sites+-- that need the full type list should use `constraintTypes` directly.+constraintType :: Constraint -> Type+constraintType c = case constraintTypes c of+  (t:_) -> t+  []    -> error "constraintType: constraint with no types"+ -- | Information about a type class data ClassInfo = ClassInfo   { classSupers  :: [String]           -- ^ Superclass names-  , classParam   :: TyVar              -- ^ Type parameter (e.g., 'a' in "class Eq a")+  , classParams  :: [TyVar]            -- ^ Type parameters (e.g. ['a'] in "class Eq a"; ['a','b'] in "class Embed a b")   , classMethods :: [(String, Type)]   -- ^ Method names and their types   } deriving (Eq, Show, Generic) +-- | Backward-compatible accessor for the first (or only) class parameter.+-- Many existing call sites assume a single-parameter class; multi-param classes+-- (Phase 5.5 Embed) need to call `classParams` directly.+classParam :: ClassInfo -> TyVar+classParam ci = case classParams ci of+  (p:_) -> p+  []    -> error "classParam: class with no type parameters"+ -- | Information about a type class instance data InstanceInfo = InstanceInfo   { instContext :: [Constraint]        -- ^ Instance context (e.g., "Eq a" in "{Eq a} Eq [a]")   , instClass   :: String              -- ^ Class name-  , instType    :: Type                -- ^ Instance type+  , instTypes   :: [Type]              -- ^ Instance types (e.g. [Integer] or [Integer, Frac Integer] for multi-param)   , instMethods :: [(String, ())]      -- ^ Method implementations (placeholder for now)   } deriving (Eq, Show, Generic) +-- | Backward-compatible accessor for the first (or only) instance type.+instType :: InstanceInfo -> Type+instType ii = case instTypes ii of+  (t:_) -> t+  []    -> error "instType: instance with no types"+ -- | Generate a fresh type variable with a given prefix freshTyVar :: String -> Int -> TyVar freshTyVar prefix n = TyVar (prefix ++ show n)@@ -121,7 +203,7 @@ -- | Get free type variables from a type freeTyVars :: Type -> Set TyVar freeTyVars TInt             = Set.empty-freeTyVars TMathExpr        = Set.empty+freeTyVars TMathValue        = Set.empty freeTyVars TPolyExpr        = Set.empty freeTyVars TTermExpr        = Set.empty freeTyVars TSymbolExpr      = Set.empty@@ -137,12 +219,62 @@ freeTyVars (TTensor t)      = freeTyVars t freeTyVars (THash k v)      = freeTyVars k `Set.union` freeTyVars v freeTyVars (TMatcher t)     = freeTyVars t+freeTyVars (TMatcherSlot s t) = freeTyVars s `Set.union` freeTyVars t freeTyVars (TFun t1 t2)     = freeTyVars t1 `Set.union` freeTyVars t2 freeTyVars (TIO t)          = freeTyVars t freeTyVars (TIORef t)       = freeTyVars t freeTyVars TPort            = Set.empty freeTyVars TAny             = Set.empty+-- New CAS types+freeTyVars TFactor          = Set.empty+freeTyVars (TTerm t ss)      = freeTyVars t `Set.union` freeTyVarsSymbolSet ss+freeTyVars (TFrac t)         = freeTyVars t+freeTyVars (TPoly t ss)     = freeTyVars t `Set.union` freeTyVarsSymbolSet ss +-- | Free type variables in a SymbolSet (used by both Term and Poly).+freeTyVarsSymbolSet :: SymbolSet -> Set TyVar+freeTyVarsSymbolSet (SymbolSetClosed _) = Set.empty+freeTyVarsSymbolSet SymbolSetOpen       = Set.empty+freeTyVarsSymbolSet (SymbolSetVar v)    = Set.singleton v++-- | Bottom-up transformation of a type: rebuild every composite node from+-- its transformed children, then apply @f@ to the rebuilt node (so @f@ sees+-- leaves as-is and composite nodes with already-transformed children).+-- Nodes produced by @f@ are not re-visited. Symbol sets of Poly/Term are+-- left untouched; a transformation that needs to rewrite them can do so in+-- its @f@ at the TPoly/TTerm node.+--+-- This is the single recursion used by the type-variable substitution+-- walkers (Env.instantiate, EnvBuilder.substituteTypeVar,+-- TypeClassExpand.applySubstsToType), which only differ in their leaf+-- function.+mapType :: (Type -> Type) -> Type -> Type+mapType f = go+  where+    go t = f (descend t)+    descend (TTuple ts)        = TTuple (map go ts)+    descend (TCollection t)    = TCollection (go t)+    descend (TInductive n ts)  = TInductive n (map go ts)+    descend (TTensor t)        = TTensor (go t)+    descend (THash k v)        = THash (go k) (go v)+    descend (TMatcher t)       = TMatcher (go t)+    descend (TMatcherSlot a b) = TMatcherSlot (go a) (go b)+    descend (TFun a b)         = TFun (go a) (go b)+    descend (TIO t)            = TIO (go t)+    descend (TIORef t)         = TIORef (go t)+    descend (TFrac t)          = TFrac (go t)+    descend (TTerm t ss)       = TTerm (go t) ss+    descend (TPoly t ss)       = TPoly (go t) ss+    descend leaf               = leaf++-- | Substitute a single type variable, leaving symbol sets untouched.+-- Shared leaf function for the substitution walkers built on 'mapType'.+substTyVar :: TyVar -> Type -> Type -> Type+substTyVar old new = mapType replace+  where+    replace (TVar v) | v == old = new+    replace t                   = t+ -- | Check if a type is a tensor type isTensorType :: Type -> Bool isTensorType (TTensor _) = True@@ -152,31 +284,112 @@ isScalarType :: Type -> Bool isScalarType = not . isTensorType +-- | Check if a type is a CAS type (Factor, Term, Frac, or Poly)+isCASType :: Type -> Bool+isCASType TFactor     = True+isCASType (TTerm _ _) = True+isCASType (TFrac _)    = True+isCASType (TPoly _ _) = True+isCASType _           = False++-- | Check if one symbol set is a subset of another+-- Used for coercive subtyping: Poly a [x] can be embedded into Poly a [x, y]+isSubsetSymbolSet :: SymbolSet -> SymbolSet -> Bool+-- Open is a superset of everything+isSubsetSymbolSet _ SymbolSetOpen = True+-- Open is only subset of itself+isSubsetSymbolSet SymbolSetOpen _ = False+-- Closed is subset if all elements are contained+isSubsetSymbolSet (SymbolSetClosed s1) (SymbolSetClosed s2) =+  all (`elem` s2) s1+-- Variables require unification+isSubsetSymbolSet (SymbolSetVar _) _ = False+isSubsetSymbolSet _ (SymbolSetVar _) = False++-- | Restriction on open atom sets: a nested Poly tower — the chain of+-- Poly/Term coefficient nesting, descending through Frac — may contain at+-- most one open symbol set @[..]@. With a single open slot every atom's+-- destination level is uniquely determined (closed sets route their atoms,+-- the open slot takes the rest), whereas a second open slot would make the+-- routing ambiguous, so reshape could not be defined. Components outside a+-- tower (tuple fields, function arguments, and so on) are separate towers,+-- each allowed its own @[..]@. Returns True when some tower in the type+-- violates the restriction.+hasAmbiguousOpenTower :: Type -> Bool+hasAmbiguousOpenTower ty = case ty of+  TPoly {} -> towerViolation ty+  TTerm {} -> towerViolation ty+  TFrac {} -> towerViolation ty+  _        -> anyComponent ty+  where+    towerViolation t =+      let (opens, base) = walk t (0 :: Int)+      in opens >= 2 || hasAmbiguousOpenTower base+    walk (TPoly inner ss) n = walk inner (n + openCount ss)+    walk (TTerm inner ss) n = walk inner (n + openCount ss)+    walk (TFrac inner)    n = walk inner n+    walk base             n = (n, base)+    openCount SymbolSetOpen = 1+    openCount _             = 0+    anyComponent t = case t of+      TTuple ts        -> any hasAmbiguousOpenTower ts+      TCollection t1   -> hasAmbiguousOpenTower t1+      TInductive _ ts  -> any hasAmbiguousOpenTower ts+      TTensor t1       -> hasAmbiguousOpenTower t1+      THash k v        -> hasAmbiguousOpenTower k || hasAmbiguousOpenTower v+      TMatcher t1      -> hasAmbiguousOpenTower t1+      TMatcherSlot a b -> hasAmbiguousOpenTower a || hasAmbiguousOpenTower b+      TFun a b         -> hasAmbiguousOpenTower a || hasAmbiguousOpenTower b+      TIO t1           -> hasAmbiguousOpenTower t1+      TIORef t1        -> hasAmbiguousOpenTower t1+      _                -> False+ -- | Convert a Type to a string name for dictionary and method naming -- This is used for generating instance dictionary names and method names -- E.g., TInt -> "Integer", TTensor TInt -> "TensorInteger"+-- | Render a type into a flat name slug used for instance-dictionary names.+-- Recursively includes inner type parameters so that e.g.+-- `Frac (Poly Integer [..])` and `Frac Integer` produce distinct names.+-- Type variables are dropped (rendered as ""), so polymorphic instances like+-- `instance Eq [a]` yield `eqCollection` rather than `eqCollectiona`.+-- Note: Integer and MathValue produce DIFFERENT names so that+-- `instance Coerce Integer Integer` and `instance Coerce MathValue MathValue`+-- are distinct (subtyping Integer ⊂ MathValue is handled separately). typeToName :: Type -> String--- Note: TInt is normalized to "MathExpr" because Integer = MathExpr in Egison-typeToName TInt = "MathExpr"  -- Integer = MathExpr, use MathExpr for dictionary names-typeToName TMathExpr = "MathExpr"+typeToName TInt = "Integer"+typeToName TMathValue = "MathValue" typeToName TFloat = "Float" typeToName TBool = "Bool" typeToName TChar = "Char" typeToName TString = "String"-typeToName (TVar (TyVar v)) = v+typeToName (TVar _) = ""  -- type variables omitted from dict names typeToName (TInductive name _) = name typeToName (TCollection t) = "Collection" ++ typeToName t typeToName (TTuple ts) = "Tuple" ++ concatMap typeToName ts typeToName (TTensor t) = "Tensor" ++ typeToName t+typeToName TFactor = "Factor"+typeToName (TTerm t ss) = "Term" ++ typeToName t ++ symbolSetToName ss+typeToName (TFrac t) = "Frac" ++ typeToName t+typeToName (TPoly t ss) = "Poly" ++ typeToName t ++ symbolSetToName ss typeToName _ = "Unknown" +-- | Render a SymbolSet into a flat name slug for dictionary lookup.+-- E.g. [sqrt 2, x] -> "_sqrt_2_x", [..] -> "_Open".+symbolSetToName :: SymbolSet -> String+symbolSetToName (SymbolSetClosed syms) =+  concatMap (\a -> '_' : sanitize (prettyTypeAtomValue a)) syms+  where+    sanitize = map (\c -> if c == ' ' || c == '(' || c == ')' then '_' else c)+symbolSetToName SymbolSetOpen = "_Open"+symbolSetToName (SymbolSetVar (TyVar v)) = "_" ++ v+ -- | Get the type constructor name only, without type parameters -- Used for generating instance dictionary names (e.g., "eqCollection" not "eqCollectiona") typeConstructorName :: Type -> String--- Note: TInt is normalized to "MathExpr" because Integer = MathExpr in Egison--- and all type class instances are defined for MathExpr, not Integer-typeConstructorName TInt = "MathExpr"  -- Integer = MathExpr, use MathExpr for dictionary names-typeConstructorName TMathExpr = "MathExpr"+-- Note: TInt is normalized to "MathValue" because Integer = MathValue in Egison+-- and all type class instances are defined for MathValue, not Integer+typeConstructorName TInt = "MathValue"  -- Integer = MathValue, use MathValue for dictionary names+typeConstructorName TMathValue = "MathValue" typeConstructorName TPolyExpr = "PolyExpr" typeConstructorName TTermExpr = "TermExpr" typeConstructorName TSymbolExpr = "SymbolExpr"@@ -192,11 +405,17 @@ typeConstructorName (TTensor _) = "Tensor" typeConstructorName (THash _ _) = "Hash" typeConstructorName (TMatcher _) = "Matcher"+typeConstructorName (TMatcherSlot _ _) = "MatcherSlot" typeConstructorName (TFun _ _) = "Fun" typeConstructorName (TIO _) = "IO" typeConstructorName (TIORef _) = "IORef" typeConstructorName TPort = "Port" typeConstructorName TAny = "Any"+-- New CAS types+typeConstructorName TFactor = "Factor"+typeConstructorName (TTerm _ _) = "Term"+typeConstructorName (TFrac _) = "Frac"+typeConstructorName (TPoly _ _) = "Poly"  -- | Sanitize method names for use in identifiers -- Converts operator symbols to alphanumeric names@@ -217,7 +436,7 @@ -- | Convert TypeExpr (from AST) to Type (internal representation) typeExprToType :: TypeExpr -> Type typeExprToType TEInt = TInt-typeExprToType TEMathExpr = TMathExpr  -- MathExpr is a primitive type+typeExprToType TEMathValue = TMathValue  -- MathValue is a primitive type typeExprToType TEFloat = TFloat typeExprToType TEBool = TBool typeExprToType TEChar = TChar@@ -230,7 +449,7 @@     TVar (TyVar name) ->        -- Special case: convert inductive type names to primitive types       case (name, ts) of-        ("MathExpr", [])   -> TMathExpr+        ("MathValue", [])   -> TMathValue         ("PolyExpr", [])   -> TPolyExpr         ("TermExpr", [])   -> TTermExpr         ("SymbolExpr", []) -> TSymbolExpr@@ -243,21 +462,75 @@ typeExprToType (TEMatrix elemT) = TTensor (typeExprToType elemT)  -- Matrix is an alias for Tensor typeExprToType (TEDiffForm elemT) = TTensor (typeExprToType elemT)  -- DiffForm is an alias for Tensor typeExprToType (TEMatcher t) = TMatcher (typeExprToType t)+typeExprToType (TEMatcherSlot s t) = TMatcherSlot (typeExprToType s) (typeExprToType t) typeExprToType (TEFun t1 t2) = TFun (typeExprToType t1) (typeExprToType t2) typeExprToType (TEIO t) = TIO (typeExprToType t) typeExprToType (TEConstrained _ t) = typeExprToType t  -- Ignore constraints typeExprToType (TEPattern t) = TInductive "Pattern" [typeExprToType t]+-- New CAS types+typeExprToType TEFactor = TFactor+typeExprToType (TETerm t ss) = TTerm (typeExprToType t) (symbolSetExprToSymbolSet ss)+typeExprToType (TEFrac t) = TFrac (typeExprToType t)+typeExprToType (TEPoly t ss) = TPoly (typeExprToType t) (symbolSetExprToSymbolSet ss) +-- | Convert AST-level SymbolSetExpr to internal SymbolSet.+symbolSetExprToSymbolSet :: SymbolSetExpr -> SymbolSet+symbolSetExprToSymbolSet (SSEClosed atoms) =+  SymbolSetClosed (map typeAtomExprToTypeAtom atoms)+symbolSetExprToSymbolSet SSEOpen = SymbolSetOpen++-- | Builtin type names that user-facing CAS declarations (`declare+-- cas-type` aliases, `declare cas-quotient` types) must not shadow.+-- Single source of truth for the name-clash validations.+reservedCasTypeNames :: Set String+reservedCasTypeNames = Set.fromList+  [ "Integer", "MathValue", "Float", "Bool", "Char", "String"+  , "Factor", "Term", "Frac", "Poly", "Tensor", "Vector", "Matrix"+  , "DiffForm", "Matcher", "MatcherSlot", "Pattern", "IO", "Symbol"+  , "PolyExpr", "TermExpr", "SymbolExpr", "IndexExpr" ]++-- | Expand `declare cas-type` transparent aliases inside a Type (Phase alpha+-- of the extensible CAS tower; design/type-cas-tower.md D3: aliases only).+-- A capitalized name in type position parses to `TVar (TyVar name)` (or is+-- concretized to `TInductive name []`), so we substitute both forms.+-- Alias bodies stored in the map are already fully expanded, hence a single+-- substitution pass suffices; we do not recurse into substituted bodies.+expandTypeAliases :: HashMap String Type -> Type -> Type+expandTypeAliases aliases ty+  | HashMap.null aliases = ty+  | otherwise = go ty+  where+    go t@(TVar (TyVar n))  = HashMap.lookupDefault t n aliases+    go t@(TInductive n []) = HashMap.lookupDefault t n aliases+    go (TInductive n ts)   = TInductive n (map go ts)+    go (TTuple ts)         = TTuple (map go ts)+    go (TCollection t)     = TCollection (go t)+    go (TTensor t)         = TTensor (go t)+    go (THash k v)         = THash (go k) (go v)+    go (TMatcher t)        = TMatcher (go t)+    go (TMatcherSlot s t)  = TMatcherSlot (go s) (go t)+    go (TFun a b)          = TFun (go a) (go b)+    go (TIO t)             = TIO (go t)+    go (TIORef t)          = TIORef (go t)+    go (TTerm t ss)        = TTerm (go t) ss+    go (TFrac t)           = TFrac (go t)+    go (TPoly t ss)        = TPoly (go t) ss+    go t                   = t+ -- | Normalize inductive type names to primitive types if applicable--- This is used to convert TInductive "MathExpr" [] to TMathExpr, etc.+-- This is used to convert TInductive "MathValue" [] to TMathValue, etc. normalizeInductiveTypes :: Type -> Type normalizeInductiveTypes (TInductive name []) = case name of-  "MathExpr"   -> TMathExpr+  "MathValue"   -> TMathValue   "PolyExpr"   -> TPolyExpr   "TermExpr"   -> TTermExpr   "SymbolExpr" -> TSymbolExpr   "IndexExpr"  -> TIndexExpr+  "Factor"     -> TFactor  -- New CAS type   _            -> TInductive name []+-- Normalize Div to TFrac+normalizeInductiveTypes (TInductive "Frac" [t]) = TFrac (normalizeInductiveTypes t)+normalizeInductiveTypes (TInductive "Term" [t]) = TTerm (normalizeInductiveTypes t) SymbolSetOpen -- Convert TInductive "Vector", "Matrix", and "DiffForm" to Tensor (they are aliases) normalizeInductiveTypes (TInductive "Vector" [t]) = TTensor (normalizeInductiveTypes t) normalizeInductiveTypes (TInductive "Matrix" [t]) = TTensor (normalizeInductiveTypes t)@@ -267,10 +540,15 @@ normalizeInductiveTypes (TCollection t) = TCollection (normalizeInductiveTypes t) normalizeInductiveTypes (THash k v) = THash (normalizeInductiveTypes k) (normalizeInductiveTypes v) normalizeInductiveTypes (TMatcher t) = TMatcher (normalizeInductiveTypes t)+normalizeInductiveTypes (TMatcherSlot s t) = TMatcherSlot (normalizeInductiveTypes s) (normalizeInductiveTypes t) normalizeInductiveTypes (TFun arg ret) = TFun (normalizeInductiveTypes arg) (normalizeInductiveTypes ret) normalizeInductiveTypes (TIO t) = TIO (normalizeInductiveTypes t) normalizeInductiveTypes (TIORef t) = TIORef (normalizeInductiveTypes t) normalizeInductiveTypes (TTensor t) = TTensor (normalizeInductiveTypes t)+-- New CAS types+normalizeInductiveTypes (TTerm t ss) = TTerm (normalizeInductiveTypes t) ss+normalizeInductiveTypes (TFrac t) = TFrac (normalizeInductiveTypes t)+normalizeInductiveTypes (TPoly t ss) = TPoly (normalizeInductiveTypes t) ss normalizeInductiveTypes t = t  -- Other types remain unchanged  -- | Capitalize first character
hs-src/Language/Egison/Type/Unify.hs view
@@ -3,6 +3,11 @@ Licence     : MIT  This module provides type unification for the Egison type system.++Three unification modes are supported via 'TensorHandling':+  - 'TensorStrict': Tensor a does NOT unify with a (for TensorMapInsertion)+  - 'TensorTopLevel': Tensor a freely unifies with a (for top-level annotations)+  - 'TensorConstraintAware': Constraint-aware Tensor handling (for general inference) -}  module Language.Egison.Type.Unify@@ -12,6 +17,7 @@   , unifyWithTopLevel   , unifyWithConstraints   , unifyMany+  , matchOneWay   , UnifyError(..)   ) where @@ -21,446 +27,375 @@                                               emptySubst, singletonSubst, applySubstConstraint) import           Language.Egison.Type.Tensor (normalizeTensorType) import           Language.Egison.Type.Types  (TyVar (..), Type (..), freeTyVars, normalizeInductiveTypes,-                                              Constraint(..))-import           Language.Egison.Type.Env    (ClassEnv, lookupInstances, InstanceInfo(..), emptyClassEnv)+                                              Constraint(..), SymbolSet(..))+import           Language.Egison.Type.Env    (ClassEnv, lookupInstances, emptyClassEnv)+import           Language.Egison.Type.Types  (instType)  -- | Unification errors data UnifyError   = OccursCheck TyVar Type        -- ^ Infinite type detected   | TypeMismatch Type Type        -- ^ Types cannot be unified+  | MatcherRigidity Type Type     -- ^ Attempt to unify two distinct Matcher types+                                  --   (matcher types are rigid; see the+                                  --   TMatcher/TMatcher case of 'unifyG')   deriving (Eq, Show) --- | Unify two types, returning a substitution if successful--- This is a wrapper around unifyWithConstraints with empty constraints--- Discards the flag since it's not needed in basic unification+--------------------------------------------------------------------------------+-- Tensor Handling Modes+--------------------------------------------------------------------------------++-- | Controls how Tensor types interact with non-Tensor types during unification.+data TensorHandling+  = TensorStrict+    -- ^ Tensor a does NOT unify with a. Used for type class instance checking+    -- in TensorMapInsertion to distinguish Tensor types from scalar types.+  | TensorTopLevel+    -- ^ Tensor a freely unifies with a at top-level definitions.+    -- According to type-tensor-simple.md: only for top-level tensor definitions,+    -- Tensor a unifying with a yields a.+  | TensorConstraintAware+    -- ^ Constraint-aware: if type variable has constraints and Tensor lacks+    -- instances for them, prefer binding to the element type instead.+  deriving (Eq)++--------------------------------------------------------------------------------+-- Public API (signatures unchanged)+--------------------------------------------------------------------------------++-- | Unify two types, returning a substitution if successful.+-- Discards the unwrap flag since it's not needed in basic unification. unify :: Type -> Type -> Either UnifyError Subst unify t1 t2 = fmap fst (unifyWithConstraints emptyClassEnv [] t1 t2) ---- | Strict unification that does NOT allow Tensor a to unify with a--- This is a wrapper around unifyStrictWithConstraints with empty constraints--- This is used for checking type class instances in TensorMapInsertion--- to ensure that Tensor types are properly distinguished from scalar types+-- | Strict unification that does NOT allow Tensor a to unify with a.+-- Used for checking type class instances in TensorMapInsertion. unifyStrict :: Type -> Type -> Either UnifyError Subst unifyStrict = unifyStrictWithConstraints emptyClassEnv [] ---- | Strict unification with type class constraints--- This is like unifyStrict but considers type class constraints when unifying type variables.+-- | Strict unification with type class constraints. -- IMPORTANT: This does NOT allow Tensor a to unify with a (strict unification).--- When unifying a constrained type variable with Tensor type, it checks if Tensor--- has instances for all the constraints. unifyStrictWithConstraints :: ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError Subst unifyStrictWithConstraints classEnv constraints t1 t2 =-  let t1' = normalizeInductiveTypes (normalizeTensorType t1)-      t2' = normalizeInductiveTypes (normalizeTensorType t2)-  in unifyStrictWithConstraints' classEnv constraints t1' t2'+  fmap fst $ unifyNormalized TensorStrict classEnv constraints t1 t2 -unifyStrictWithConstraints' :: ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError Subst--- Same types unify trivially-unifyStrictWithConstraints' _ _ TInt TInt = Right emptySubst-unifyStrictWithConstraints' _ _ TMathExpr TMathExpr = Right emptySubst-unifyStrictWithConstraints' _ _ TPolyExpr TPolyExpr = Right emptySubst-unifyStrictWithConstraints' _ _ TTermExpr TTermExpr = Right emptySubst-unifyStrictWithConstraints' _ _ TSymbolExpr TSymbolExpr = Right emptySubst-unifyStrictWithConstraints' _ _ TIndexExpr TIndexExpr = Right emptySubst-unifyStrictWithConstraints' _ _ TFloat TFloat = Right emptySubst-unifyStrictWithConstraints' _ _ TBool TBool = Right emptySubst-unifyStrictWithConstraints' _ _ TChar TChar = Right emptySubst-unifyStrictWithConstraints' _ _ TString TString = Right emptySubst+-- | Unify two types, allowing Tensor a to unify with a at top-level definitions.+-- According to type-tensor-simple.md: only for top-level tensor definitions.+unifyWithTopLevel :: Type -> Type -> Either UnifyError Subst+unifyWithTopLevel t1 t2 =+  fmap fst $ unifyNormalized TensorTopLevel emptyClassEnv [] t1 t2 --- Special rule: TInt and TMathExpr unify-unifyStrictWithConstraints' _ _ TInt TMathExpr = Right emptySubst-unifyStrictWithConstraints' _ _ TMathExpr TInt = Right emptySubst+-- | Unify two types while considering type class constraints.+-- Returns (Subst, Bool) where Bool indicates if Tensor was unwrapped.+unifyWithConstraints :: ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError (Subst, Bool)+unifyWithConstraints = unifyNormalized TensorConstraintAware --- Type variables - use constraint-aware strict unification-unifyStrictWithConstraints' classEnv constraints (TVar v) t =-  unifyVarStrictWithConstraints classEnv constraints v t-unifyStrictWithConstraints' classEnv constraints t (TVar v) =-  unifyVarStrictWithConstraints classEnv constraints v t+-- | Unify a list of type pairs.+unifyMany :: [Type] -> [Type] -> Either UnifyError Subst+unifyMany ts1 ts2 =+  fmap fst $ unifyManyG TensorConstraintAware emptyClassEnv [] ts1 ts2 -unifyStrictWithConstraints' classEnv constraints (TTuple ts1) (TTuple ts2)-  | length ts1 == length ts2 = unifyManyStrictWithConstraints classEnv constraints ts1 ts2-  | otherwise = Left $ TypeMismatch (TTuple ts1) (TTuple ts2)+--------------------------------------------------------------------------------+-- Normalization Entry Point+-------------------------------------------------------------------------------- -unifyStrictWithConstraints' classEnv constraints (TCollection t1) (TCollection t2) =-  unifyStrictWithConstraints classEnv constraints t1 t2+-- | Trivial success: empty substitution, no Tensor unwrapping.+ok :: Either UnifyError (Subst, Bool)+ok = Right (emptySubst, False) --- Inductive types-unifyStrictWithConstraints' classEnv constraints (TInductive n1 ts1) (TInductive n2 ts2)-  | n1 == n2 && length ts1 == length ts2 = unifyManyStrictWithConstraints classEnv constraints ts1 ts2-  | otherwise = Left $ TypeMismatch (TInductive n1 ts1) (TInductive n2 ts2)+-- | Normalize types and delegate to core unification.+unifyNormalized :: TensorHandling -> ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError (Subst, Bool)+unifyNormalized mode classEnv constraints t1 t2 =+  let t1' = normalizeInductiveTypes (normalizeTensorType t1)+      t2' = normalizeInductiveTypes (normalizeTensorType t2)+  in unifyG mode classEnv constraints t1' t2' -unifyStrictWithConstraints' classEnv constraints (THash k1 v1) (THash k2 v2) = do-  s1 <- unifyStrictWithConstraints classEnv constraints k1 k2-  let constraints' = map (applySubstConstraint s1) constraints-  s2 <- unifyStrictWithConstraints classEnv constraints' (applySubst s1 v1) (applySubst s1 v2)-  Right $ composeSubst s2 s1+--------------------------------------------------------------------------------+-- Generic Core Unification+-------------------------------------------------------------------------------- -unifyStrictWithConstraints' classEnv constraints (TMatcher t1) (TMatcher t2) =-  unifyStrictWithConstraints classEnv constraints t1 t2+-- | Core unification function parametrized by TensorHandling mode.+-- All public unification variants delegate to this single function, eliminating+-- the previous code duplication across three nearly-identical implementations.+unifyG :: TensorHandling -> ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError (Subst, Bool) -unifyStrictWithConstraints' classEnv constraints (TFun a1 r1) (TFun a2 r2) = do-  s1 <- unifyStrictWithConstraints classEnv constraints a1 a2-  let constraints' = map (applySubstConstraint s1) constraints-  s2 <- unifyStrictWithConstraints classEnv constraints' (applySubst s1 r1) (applySubst s1 r2)-  Right $ composeSubst s2 s1+-- Same types unify trivially+unifyG _ _ _ TInt TInt = ok+unifyG _ _ _ TMathValue TMathValue = ok+unifyG _ _ _ TPolyExpr TPolyExpr = ok+unifyG _ _ _ TTermExpr TTermExpr = ok+unifyG _ _ _ TSymbolExpr TSymbolExpr = ok+unifyG _ _ _ TIndexExpr TIndexExpr = ok+unifyG _ _ _ TFloat TFloat = ok+unifyG _ _ _ TBool TBool = ok+unifyG _ _ _ TChar TChar = ok+unifyG _ _ _ TString TString = ok -unifyStrictWithConstraints' classEnv constraints (TIO t1) (TIO t2) =-  unifyStrictWithConstraints classEnv constraints t1 t2+-- Special rule: TInt and TMathValue unify+unifyG _ _ _ TInt TMathValue = ok+unifyG _ _ _ TMathValue TInt = ok -unifyStrictWithConstraints' classEnv constraints (TIORef t1) (TIORef t2) =-  unifyStrictWithConstraints classEnv constraints t1 t2+-- Phase 5.5 (simplified subtype unification): all CAS-family types+-- (Factor / Frac / Poly) unify with MathValue and with TInt (=MathValue).+-- This is the "every CAS type is a subtype of MathValue" relationship from+-- the design's type-inclusion graph. The runtime values are all CASValue+-- so this is sound at the value level; full Embed/coerce machinery (with+-- runtime checks) is still pending.+unifyG _ _ _ TMathValue TFactor   = ok+unifyG _ _ _ TFactor    TMathValue = ok+unifyG _ _ _ TInt        TFactor   = ok+unifyG _ _ _ TFactor     TInt      = ok+unifyG _ _ _ TMathValue (TTerm _ _) = ok+unifyG _ _ _ (TTerm _ _) TMathValue = ok+unifyG _ _ _ TInt       (TTerm _ _) = ok+unifyG _ _ _ (TTerm _ _) TInt       = ok+unifyG _ _ _ TMathValue (TFrac _)  = ok+unifyG _ _ _ (TFrac _)  TMathValue = ok+unifyG _ _ _ TInt       (TFrac _)  = ok+unifyG _ _ _ (TFrac _)  TInt       = ok+unifyG _ _ _ TMathValue (TPoly _ _) = ok+unifyG _ _ _ (TPoly _ _) TMathValue = ok+unifyG _ _ _ TInt        (TPoly _ _) = ok+unifyG _ _ _ (TPoly _ _) TInt        = ok+-- Cross-level widening: any Frac chain unifies with any Poly chain via reshape+-- (e.g. `def e2 : Poly (Frac Integer) [..] := e1` where e1 : Frac Integer).+unifyG _ _ _ (TFrac _)   (TPoly _ _) = ok+unifyG _ _ _ (TPoly _ _) (TFrac _)   = ok+-- Factor widening into Frac/Poly chains (e.g. `def e4 : Frac (Poly Integer [x]) := x`).+unifyG _ _ _ TFactor     (TFrac _)   = ok+unifyG _ _ _ (TFrac _)   TFactor     = ok+unifyG _ _ _ TFactor     (TPoly _ _) = ok+unifyG _ _ _ (TPoly _ _) TFactor     = ok -unifyStrictWithConstraints' _ _ TPort TPort = Right emptySubst+-- Type variables: delegated to mode-specific handler+unifyG mode ce cs (TVar v) t = unifyVarG mode ce cs v t+unifyG mode ce cs t (TVar v) = unifyVarG mode ce cs v t --- Tensor types - STRICT: Tensor a does NOT unify with a-unifyStrictWithConstraints' classEnv constraints (TTensor t1) (TTensor t2) =-  unifyStrictWithConstraints classEnv constraints t1 t2+-- Tuples+unifyG mode ce cs (TTuple ts1) (TTuple ts2)+  | length ts1 == length ts2 = unifyManyG mode ce cs ts1 ts2+  | otherwise = Left $ TypeMismatch (TTuple ts1) (TTuple ts2) --- TAny unifies with anything-unifyStrictWithConstraints' _ _ TAny _ = Right emptySubst-unifyStrictWithConstraints' _ _ _ TAny = Right emptySubst+-- Collections+unifyG mode ce cs (TCollection t1) (TCollection t2) =+  unifyNormalized mode ce cs t1 t2 --- Mismatched types-unifyStrictWithConstraints' _ _ t1 t2 = Left $ TypeMismatch t1 t2+-- Inductive types+unifyG mode ce cs (TInductive n1 ts1) (TInductive n2 ts2)+  | n1 == n2 && length ts1 == length ts2 = unifyManyG mode ce cs ts1 ts2+  | otherwise = Left $ TypeMismatch (TInductive n1 ts1) (TInductive n2 ts2) --- | Unify a type variable with a type using strict unification with constraints--- IMPORTANT: This is STRICT - Tensor a does NOT unify with a-unifyVarStrictWithConstraints :: ClassEnv -> [Constraint] -> TyVar -> Type -> Either UnifyError Subst-unifyVarStrictWithConstraints classEnv constraints v t-  | TVar v == t = Right emptySubst-  | otherwise = case t of-      -- Tensor type: check if the type variable's constraints allow Tensor-      TTensor elemType ->-        let varConstraints = filter (\(Constraint _ constraintType) -> constraintType == TVar v) constraints-        in if null varConstraints-           then-             -- No constraints: can bind to Tensor (with occurs check)-             if v `Set.member` freeTyVars t-             then Left $ OccursCheck v t-             else Right $ singletonSubst v t-           else-             -- Has constraints: check if Tensor has instances for ALL of them-             if all (hasInstanceForTensorType classEnv elemType) varConstraints-             then-               -- All constraints satisfied: can bind to Tensor-               if v `Set.member` freeTyVars t-               then Left $ OccursCheck v t-               else Right $ singletonSubst v t-             else-               -- Some constraint not satisfied by Tensor: cannot unify (strict)-               Left $ TypeMismatch (TVar v) t-      _ ->-        -- Non-Tensor type: regular occurs check and bind-        if v `Set.member` freeTyVars t-        then Left $ OccursCheck v t-        else Right $ singletonSubst v t+-- Hash types (two components with substitution threading)+unifyG mode ce cs (THash k1 v1) (THash k2 v2) = do+  (s1, f1) <- unifyNormalized mode ce cs k1 k2+  let cs' = map (applySubstConstraint s1) cs+  (s2, f2) <- unifyNormalized mode ce cs' (applySubst s1 v1) (applySubst s1 v2)+  Right (composeSubst s2 s1, f1 || f2) --- | Unify multiple type pairs with strict unification and constraints-unifyManyStrictWithConstraints :: ClassEnv -> [Constraint] -> [Type] -> [Type] -> Either UnifyError Subst-unifyManyStrictWithConstraints _ _ [] [] = Right emptySubst-unifyManyStrictWithConstraints classEnv constraints (t1:ts1) (t2:ts2) = do-  s1 <- unifyStrictWithConstraints classEnv constraints t1 t2-  let constraints' = map (applySubstConstraint s1) constraints-  s2 <- unifyManyStrictWithConstraints classEnv constraints' (map (applySubst s1) ts1) (map (applySubst s1) ts2)-  Right $ composeSubst s2 s1-unifyManyStrictWithConstraints _ _ _ _ = Left $ TypeMismatch (TTuple []) (TTuple [])+-- Matcher-Tuple special rule (ConstraintAware mode only)+unifyG TensorConstraintAware ce cs (TMatcher b) (TTuple ts) =+  unifyMatcherWithTupleG ce cs b ts+unifyG TensorConstraintAware ce cs (TTuple ts) (TMatcher b) =+  unifyMatcherWithTupleG ce cs b ts --- | Unify a type variable with a type-unifyVar :: TyVar -> Type -> Either UnifyError Subst-unifyVar v t-  | TVar v == t = Right emptySubst-  | occursIn v t = Left $ OccursCheck v t-  | otherwise = Right $ singletonSubst v t+-- COERCE-SLOT-TUPLE: a tuple of matchers filling a product MatcherSlot+-- (ConstraintAware mode only, reusing the Matcher/Tuple machinery).+unifyG TensorConstraintAware ce cs (TMatcherSlot ts tt) (TTuple tys) =+  coerceSlotTuple TensorConstraintAware ce cs ts tt tys+unifyG TensorConstraintAware ce cs (TTuple tys) (TMatcherSlot ts tt) =+  coerceSlotTuple TensorConstraintAware ce cs ts tt tys --- | Occurs check: ensure a type variable doesn't occur in a type--- This prevents infinite types like a = [a]-occursIn :: TyVar -> Type -> Bool-occursIn v t = v `Set.member` freeTyVars t+-- Matcher types: RIGID -- two Matcher types are compatible only when their+-- parameters are already equal; their unification is forbidden.  A matcher+-- value's structural capability is fixed by its definition and judged from+-- its *intrinsic* type ('coerceMatcherToSlot' / 'matchOneWay'), so letting+-- @Matcher t1@ unify with @Matcher t2@ would specialize an intrinsically+-- general matcher's type -- e.g. give @something : Matcher b@ the type+-- @Matcher [Integer]@ via the list literal @[something, list integer]@ --+-- and a later match site would trust a structural capability the runtime+-- value does not have (well-typed but stuck).  Consequences: a polymorphic+-- matcher cannot be specialized by annotation (write a concrete matcher+-- literal instead of @def integer : Matcher Integer := eq@), and matcher-+-- consuming function parameters must be slot-typed (@m : MatcherSlot a a@,+-- filled by 'coerceMatcherToSlot' at the call site) rather than+-- @m : Matcher a@.+unifyG _ _ _ (TMatcher t1) (TMatcher t2)+  | t1 == t2  = ok+  | otherwise = Left $ MatcherRigidity (TMatcher t1) (TMatcher t2) --- | Unify Matcher b with (t1, t2, ...) by treating each ti as Matcher ci--- Result: b = (c1, c2, ...) where ti unifies with Matcher ci-unifyMatcherWithTuple :: Type -> [Type] -> Either UnifyError Subst-unifyMatcherWithTuple b ts = do-  -- Process each element: extract inner type or create constraint-  (innerTypes, s1) <- unifyEachAsMatcher ts emptySubst-  -- Now unify b with (c1, c2, ...)-  let tupleType = TTuple innerTypes-  s2 <- unify (applySubst s1 b) tupleType-  Right $ composeSubst s2 s1-  where-    -- Unify each type in the tuple with Matcher ci, extracting ci-    unifyEachAsMatcher :: [Type] -> Subst -> Either UnifyError ([Type], Subst)-    unifyEachAsMatcher [] s = Right ([], s)-    unifyEachAsMatcher (t:rest) s = do-      let t' = applySubst s t-      (innerType, s1) <- case t' of-        -- If already Matcher c, extract c-        TMatcher inner -> Right (inner, emptySubst)-        -- If type variable, unify it with Matcher (fresh variable)-        TVar v -> do-          -- Generate a new variable name for the inner type-          let innerVar = TyVar (getTyVarName v ++ "'")-              innerType = TVar innerVar-          s' <- unify t' (TMatcher innerType)-          Right (applySubst s' innerType, s')-        -- Other types cannot be unified with Matcher-        _ -> Left $ TypeMismatch (TMatcher (TVar (TyVar "?"))) t'-      -      let s2 = composeSubst s1 s-      (restInnerTypes, s3) <- unifyEachAsMatcher rest s2-      Right (applySubst s3 innerType : restInnerTypes, s3)-    -    getTyVarName :: TyVar -> String-    getTyVarName (TyVar name) = name+-- MatcherSlot types (two components: structural type and target type)+unifyG mode ce cs (TMatcherSlot s1 t1) (TMatcherSlot s2 t2) = do+  (sub1, f1) <- unifyNormalized mode ce cs s1 s2+  let cs' = map (applySubstConstraint sub1) cs+  (sub2, f2) <- unifyNormalized mode ce cs' (applySubst sub1 t1) (applySubst sub1 t2)+  Right (composeSubst sub2 sub1, f1 || f2) --- | Unify two types, allowing Tensor a to unify with a at top-level definitions--- This is used only for top-level definitions with type annotations--- According to type-tensor-simple.md: "トップレベル定義のテンソルについてのみ、Tensor a型が a型とunifyするとa型になる。"-unifyWithTopLevel :: Type -> Type -> Either UnifyError Subst-unifyWithTopLevel t1 t2 =-  let t1' = normalizeInductiveTypes (normalizeTensorType t1)-      t2' = normalizeInductiveTypes (normalizeTensorType t2)-  in unifyWithTopLevel' t1' t2'+-- COERCE-MATCHER-TO-SLOT: a Matcher value filling a MatcherSlot consumer position+-- (bidirectional: the Matcher value may appear on either side of the unification).+-- Dual check (see 'coerceMatcherToSlot'): structural admissibility, checked one-way on the+-- intrinsic matcher type BEFORE the target unification, plus target unifiability.+unifyG mode ce cs (TMatcher tm) (TMatcherSlot ts tt) =+  coerceMatcherToSlot mode ce cs tm ts tt+unifyG mode ce cs (TMatcherSlot ts tt) (TMatcher tm) =+  coerceMatcherToSlot mode ce cs tm ts tt -unifyWithTopLevel' :: Type -> Type -> Either UnifyError Subst--- Same types unify trivially-unifyWithTopLevel' TInt TInt = Right emptySubst-unifyWithTopLevel' TMathExpr TMathExpr = Right emptySubst-unifyWithTopLevel' TPolyExpr TPolyExpr = Right emptySubst-unifyWithTopLevel' TTermExpr TTermExpr = Right emptySubst-unifyWithTopLevel' TSymbolExpr TSymbolExpr = Right emptySubst-unifyWithTopLevel' TIndexExpr TIndexExpr = Right emptySubst-unifyWithTopLevel' TFloat TFloat = Right emptySubst-unifyWithTopLevel' TBool TBool = Right emptySubst-unifyWithTopLevel' TChar TChar = Right emptySubst-unifyWithTopLevel' TString TString = Right emptySubst+-- Function types (two components with substitution threading)+unifyG mode ce cs (TFun a1 r1) (TFun a2 r2) = do+  (s1, f1) <- unifyNormalized mode ce cs a1 a2+  let cs' = map (applySubstConstraint s1) cs+  (s2, f2) <- unifyNormalized mode ce cs' (applySubst s1 r1) (applySubst s1 r2)+  Right (composeSubst s2 s1, f1 || f2) --- Special rule: TInt and TMathExpr unify to TMathExpr-unifyWithTopLevel' TInt TMathExpr = Right emptySubst-unifyWithTopLevel' TMathExpr TInt = Right emptySubst+-- IO types+unifyG mode ce cs (TIO t1) (TIO t2) =+  unifyNormalized mode ce cs t1 t2 --- Type variables-unifyWithTopLevel' (TVar v) t = unifyVar v t-unifyWithTopLevel' t (TVar v) = unifyVar v t+-- IORef types+unifyG mode ce cs (TIORef t1) (TIORef t2) =+  unifyNormalized mode ce cs t1 t2 -unifyWithTopLevel' (TTuple ts1) (TTuple ts2)-  | length ts1 == length ts2 = unifyManyWithTopLevel ts1 ts2-  | otherwise = Left $ TypeMismatch (TTuple ts1) (TTuple ts2)+-- Port type+unifyG _ _ _ TPort TPort = ok -unifyWithTopLevel' (TCollection t1) (TCollection t2) = unifyWithTopLevel t1 t2+-- CAS types+unifyG _ _ _ TFactor TFactor = ok --- Inductive types-unifyWithTopLevel' (TInductive n1 ts1) (TInductive n2 ts2)-  | n1 == n2 && length ts1 == length ts2 = unifyManyWithTopLevel ts1 ts2-  | otherwise = Left $ TypeMismatch (TInductive n1 ts1) (TInductive n2 ts2)+unifyG mode ce cs (TTerm t1 ss1) (TTerm t2 ss2) = do+  (s1, f1) <- unifyNormalized mode ce cs t1 t2+  case unifySymbolSets ss1 ss2 of+    Just _  -> Right (s1, f1)+    Nothing -> Left $ TypeMismatch (TTerm t1 ss1) (TTerm t2 ss2) -unifyWithTopLevel' (THash k1 v1) (THash k2 v2) = do-  s1 <- unifyWithTopLevel k1 k2-  s2 <- unifyWithTopLevel (applySubst s1 v1) (applySubst s1 v2)-  Right $ composeSubst s2 s1+unifyG mode ce cs (TFrac t1) (TFrac t2) =+  unifyNormalized mode ce cs t1 t2 -unifyWithTopLevel' (TMatcher t1) (TMatcher t2) = unifyWithTopLevel t1 t2+unifyG mode ce cs (TPoly t1 ss1) (TPoly t2 ss2) = do+  -- First unify the coefficient types+  (s1, f1) <- unifyNormalized mode ce cs t1 t2+  -- Then unify the symbol sets+  case unifySymbolSets ss1 ss2 of+    Just _  -> Right (s1, f1)+    Nothing -> Left $ TypeMismatch (TPoly t1 ss1) (TPoly t2 ss2) -unifyWithTopLevel' (TFun a1 r1) (TFun a2 r2) = do-  s1 <- unifyWithTopLevel a1 a2-  s2 <- unifyWithTopLevel (applySubst s1 r1) (applySubst s1 r2)-  Right $ composeSubst s2 s1+-- Tensor types: both Tensor — same for all modes+unifyG mode ce cs (TTensor t1) (TTensor t2) =+  unifyNormalized mode ce cs t1 t2 -unifyWithTopLevel' (TIO t1) (TIO t2) = unifyWithTopLevel t1 t2+-- Tensor vs non-Tensor: TopLevel allows unwrapping+unifyG TensorTopLevel _ _ (TTensor t1) t2 = do+  (s, _) <- unifyNormalized TensorTopLevel emptyClassEnv [] t1 t2+  Right (s, True)+unifyG TensorTopLevel _ _ t1 (TTensor t2) = do+  (s, _) <- unifyNormalized TensorTopLevel emptyClassEnv [] t1 t2+  Right (s, True) -unifyWithTopLevel' (TIORef t1) (TIORef t2) = unifyWithTopLevel t1 t2+-- Tensor vs non-Tensor: ConstraintAware uses constraint-aware logic+unifyG TensorConstraintAware ce cs (TTensor t1) t2 =+  unifyTensorWithConstraints ce cs t1 t2+unifyG TensorConstraintAware ce cs t1 (TTensor t2) =+  unifyTensorWithConstraints ce cs t2 t1 -unifyWithTopLevel' TPort TPort = Right emptySubst+-- TensorStrict: Tensor vs non-Tensor falls through to mismatch below  -- TAny unifies with anything-unifyWithTopLevel' TAny _ = Right emptySubst-unifyWithTopLevel' _ TAny = Right emptySubst---- Tensor types--- Tensor a and Tensor b unify if a and b unify-unifyWithTopLevel' (TTensor t1) (TTensor t2) = unifyWithTopLevel t1 t2--- Tensor a and a can unify as a (only at top-level definitions)--- Tensor MathExpr can unifies with MathExpr as MathExpr-unifyWithTopLevel' (TTensor t1) t2 = do-  s <- unifyWithTopLevel t1 t2-  -- Return substitution that unifies t1 with t2, result type is t2 (scalar)-  Right s--unifyWithTopLevel' t1 (TTensor t2) = do-  s <- unifyWithTopLevel t1 t2-  -- Return substitution that unifies t1 with t2, result type is t1 (scalar)-  Right s+unifyG _ _ _ TAny _ = ok+unifyG _ _ _ _ TAny = ok  -- Mismatched types-unifyWithTopLevel' t1 t2 = Left $ TypeMismatch t1 t2---- | Unify a list of type pairs with top-level tensor unification-unifyManyWithTopLevel :: [Type] -> [Type] -> Either UnifyError Subst-unifyManyWithTopLevel [] [] = Right emptySubst-unifyManyWithTopLevel (t1:ts1) (t2:ts2) = do-  s1 <- unifyWithTopLevel t1 t2-  s2 <- unifyManyWithTopLevel (map (applySubst s1) ts1) (map (applySubst s1) ts2)-  Right $ composeSubst s2 s1-unifyManyWithTopLevel _ _ = Left $ TypeMismatch (TTuple []) (TTuple [])  -- Length mismatch---- | Unify a list of type pairs-unifyMany :: [Type] -> [Type] -> Either UnifyError Subst-unifyMany [] [] = Right emptySubst-unifyMany (t1:ts1) (t2:ts2) = do-  s1 <- unify t1 t2-  s2 <- unifyMany (map (applySubst s1) ts1) (map (applySubst s1) ts2)-  Right $ composeSubst s2 s1-unifyMany _ _ = Left $ TypeMismatch (TTuple []) (TTuple [])  -- Length mismatch+unifyG _ _ _ t1 t2 = Left $ TypeMismatch t1 t2  ----------------------------------------------------------------------------------- Constraint-Aware Unification+-- Generic Unify-Many -------------------------------------------------------------------------------- --- | Unify two types while considering type class constraints--- This function chooses unifiers that satisfy type class constraints--- Specifically, when unifying Tensor a with a constrained type variable t:---   - If C t constraint exists and C (Tensor a) is not satisfiable,---     prefer t = a over t = Tensor a--- Returns (Subst, Bool) where Bool indicates if Tensor was unwrapped during unification-unifyWithConstraints :: ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError (Subst, Bool)-unifyWithConstraints classEnv constraints t1 t2 =-  let t1' = normalizeInductiveTypes (normalizeTensorType t1)-      t2' = normalizeInductiveTypes (normalizeTensorType t2)-  in unifyWithConstraints' classEnv constraints t1' t2'--unifyWithConstraints' :: ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError (Subst, Bool)--- Same types unify trivially-unifyWithConstraints' _ _ TInt TInt = Right (emptySubst, False)-unifyWithConstraints' _ _ TMathExpr TMathExpr = Right (emptySubst, False)-unifyWithConstraints' _ _ TPolyExpr TPolyExpr = Right (emptySubst, False)-unifyWithConstraints' _ _ TTermExpr TTermExpr = Right (emptySubst, False)-unifyWithConstraints' _ _ TSymbolExpr TSymbolExpr = Right (emptySubst, False)-unifyWithConstraints' _ _ TIndexExpr TIndexExpr = Right (emptySubst, False)-unifyWithConstraints' _ _ TFloat TFloat = Right (emptySubst, False)-unifyWithConstraints' _ _ TBool TBool = Right (emptySubst, False)-unifyWithConstraints' _ _ TChar TChar = Right (emptySubst, False)-unifyWithConstraints' _ _ TString TString = Right (emptySubst, False)---- Special rule: TInt and TMathExpr unify to TMathExpr-unifyWithConstraints' _ _ TInt TMathExpr = Right (emptySubst, False)-unifyWithConstraints' _ _ TMathExpr TInt = Right (emptySubst, False)---- Type variables - with constraint-aware Tensor handling-unifyWithConstraints' classEnv constraints (TVar v) t =-  unifyVarWithConstraints classEnv constraints v t-unifyWithConstraints' classEnv constraints t (TVar v) =-  unifyVarWithConstraints classEnv constraints v t--unifyWithConstraints' classEnv constraints (TTuple ts1) (TTuple ts2)-  | length ts1 == length ts2 = unifyManyWithConstraints classEnv constraints ts1 ts2-  | otherwise = Left $ TypeMismatch (TTuple ts1) (TTuple ts2)--unifyWithConstraints' classEnv constraints (TCollection t1) (TCollection t2) = do-  (s, flag) <- unifyWithConstraints classEnv constraints t1 t2-  Right (s, flag)---- Inductive types-unifyWithConstraints' classEnv constraints (TInductive n1 ts1) (TInductive n2 ts2)-  | n1 == n2 && length ts1 == length ts2 = unifyManyWithConstraints classEnv constraints ts1 ts2-  | otherwise = Left $ TypeMismatch (TInductive n1 ts1) (TInductive n2 ts2)--unifyWithConstraints' classEnv constraints (THash k1 v1) (THash k2 v2) = do-  (s1, flag1) <- unifyWithConstraints classEnv constraints k1 k2-  (s2, flag2) <- unifyWithConstraints classEnv (map (applySubstConstraint s1) constraints) (applySubst s1 v1) (applySubst s1 v2)-  Right (composeSubst s2 s1, flag1 || flag2)---- Special rule: Matcher b unifies with (t1, t2, ...)--- by treating each ti as Matcher ci, resulting in b = (c1, c2, ...)-unifyWithConstraints' classEnv constraints (TMatcher b) (TTuple ts) =-  unifyMatcherWithTupleWithConstraints classEnv constraints b ts-unifyWithConstraints' classEnv constraints (TTuple ts) (TMatcher b) =-  unifyMatcherWithTupleWithConstraints classEnv constraints b ts--unifyWithConstraints' classEnv constraints (TMatcher t1) (TMatcher t2) = do-  (s, flag) <- unifyWithConstraints classEnv constraints t1 t2-  Right (s, flag)--unifyWithConstraints' classEnv constraints (TFun a1 r1) (TFun a2 r2) = do-  (s1, flag1) <- unifyWithConstraints classEnv constraints a1 a2-  (s2, flag2) <- unifyWithConstraints classEnv (map (applySubstConstraint s1) constraints) (applySubst s1 r1) (applySubst s1 r2)-  Right (composeSubst s2 s1, flag1 || flag2)--unifyWithConstraints' classEnv constraints (TIO t1) (TIO t2) = do-  (s, flag) <- unifyWithConstraints classEnv constraints t1 t2-  Right (s, flag)--unifyWithConstraints' classEnv constraints (TIORef t1) (TIORef t2) = do-  (s, flag) <- unifyWithConstraints classEnv constraints t1 t2-  Right (s, flag)--unifyWithConstraints' _ _ TPort TPort = Right (emptySubst, False)+-- | Unify multiple type pairs generically.+unifyManyG :: TensorHandling -> ClassEnv -> [Constraint] -> [Type] -> [Type] -> Either UnifyError (Subst, Bool)+unifyManyG _ _ _ [] [] = ok+unifyManyG mode ce cs (t1:ts1) (t2:ts2) = do+  (s1, f1) <- unifyNormalized mode ce cs t1 t2+  let cs' = map (applySubstConstraint s1) cs+  (s2, f2) <- unifyManyG mode ce cs' (map (applySubst s1) ts1) (map (applySubst s1) ts2)+  Right (composeSubst s2 s1, f1 || f2)+unifyManyG _ _ _ _ _ = Left $ TypeMismatch (TTuple []) (TTuple []) --- Tensor types - both Tensor-unifyWithConstraints' classEnv constraints (TTensor t1) (TTensor t2) = do-  (s, flag) <- unifyWithConstraints classEnv constraints t1 t2-  Right (s, flag)+--------------------------------------------------------------------------------+-- Variable Unification (mode-specific dispatch)+-------------------------------------------------------------------------------- --- IMPORTANT: Constraint-aware handling for Tensor <-> non-Tensor--- When unifying Tensor a with non-Tensor, prefer non-Tensor if it satisfies constraints-unifyWithConstraints' classEnv constraints (TTensor t1) t2 =-  unifyTensorWithConstraints classEnv constraints t1 t2-unifyWithConstraints' classEnv constraints t1 (TTensor t2) =-  unifyTensorWithConstraints classEnv constraints t2 t1+-- | Unify a type variable with a type, delegating to mode-specific logic.+unifyVarG :: TensorHandling -> ClassEnv -> [Constraint] -> TyVar -> Type -> Either UnifyError (Subst, Bool)+unifyVarG TensorStrict ce cs v t =+  fmap (\s -> (s, False)) $ unifyVarStrict ce cs v t+unifyVarG TensorTopLevel _ _ v t =+  fmap (\s -> (s, False)) $ unifyVarSimple v t+unifyVarG TensorConstraintAware ce cs v t =+  unifyVarConstraintAware ce cs v t --- TAny unifies with anything-unifyWithConstraints' _ _ TAny _ = Right (emptySubst, False)-unifyWithConstraints' _ _ _ TAny = Right (emptySubst, False)+-- | Simple variable unification (no constraint or Tensor logic).+unifyVarSimple :: TyVar -> Type -> Either UnifyError Subst+unifyVarSimple v t+  | TVar v == t = Right emptySubst+  | v `Set.member` freeTyVars t = Left $ OccursCheck v t+  | otherwise = Right $ singletonSubst v t --- Mismatched types-unifyWithConstraints' _ _ t1 t2 = Left $ TypeMismatch t1 t2+-- | Strict variable unification with constraints.+-- Tensor a does NOT unify with a unless all constraints are satisfied by Tensor.+unifyVarStrict :: ClassEnv -> [Constraint] -> TyVar -> Type -> Either UnifyError Subst+unifyVarStrict classEnv constraints v t+  | TVar v == t = Right emptySubst+  | otherwise = case t of+      TTensor elemType ->+        let varConstraints = filter (\c -> TVar v `elem` constraintTypes c) constraints+        in if null varConstraints+           then occursCheckAndBind v t+           else if all (hasInstanceForTensorType classEnv elemType) varConstraints+                then occursCheckAndBind v t+                else Left $ TypeMismatch (TVar v) t+      _ -> occursCheckAndBind v t --- | Unify type variable with another type, considering constraints--- Note: occurs check is deferred to handle cases like unifying t0 with Tensor t0--- when t0 has constraints (e.g., {Num t0}) and there's no Num (Tensor t0) instance.--- In such cases, we bind t0 to the element type (t0 itself), which is identity.--- Returns (Subst, Bool) where Bool indicates if Tensor was unwrapped during unification-unifyVarWithConstraints :: ClassEnv -> [Constraint] -> TyVar -> Type -> Either UnifyError (Subst, Bool)-unifyVarWithConstraints classEnv constraints v t+-- | Constraint-aware variable unification.+-- Returns (Subst, Bool) where Bool indicates if Tensor was unwrapped.+unifyVarConstraintAware :: ClassEnv -> [Constraint] -> TyVar -> Type -> Either UnifyError (Subst, Bool)+unifyVarConstraintAware classEnv constraints v t   | TVar v == t = Right (emptySubst, False)   | otherwise = case t of-      -- Special handling for Tensor types with constraints       TTensor elemType ->-        -- Check if the type variable has constraints-        let varConstraints = filter (\(Constraint _ constraintType) -> constraintType == TVar v) constraints+        let varConstraints = filter (\c -> TVar v `elem` constraintTypes c) constraints         in if null varConstraints-           then-             -- No constraints on this variable, bind to Tensor (need occurs check)-             if v `Set.member` freeTyVars t-             then Left $ OccursCheck v t-             else Right (singletonSubst v t, False)-           else-             -- Has constraints: check if Tensor has instances for all of them-             if all (hasInstanceForTensorType classEnv elemType) varConstraints-             then-               -- All constraints have Tensor instances, bind to Tensor (need occurs check)-               if v `Set.member` freeTyVars t-               then Left $ OccursCheck v t-               else Right (singletonSubst v t, False)-             else-               -- Some constraint lacks Tensor instance, bind to element type instead-               -- This allows tensorMap to handle the Tensor -> scalar conversion-               -- Special case: if v == elemType (e.g., t0 with Tensor t0), return identity-               -- FLAG: Set to True because Tensor was unwrapped-               if TVar v == elemType-               then Right (emptySubst, True)-               else if v `Set.member` freeTyVars elemType-                    then Left $ OccursCheck v elemType-                    else Right (singletonSubst v elemType, True)+           then fmap (\s -> (s, False)) $ occursCheckAndBind v t+           else if all (hasInstanceForTensorType classEnv elemType) varConstraints+                then fmap (\s -> (s, False)) $ occursCheckAndBind v t+                else+                  -- Some constraint lacks Tensor instance, bind to element type instead.+                  -- This allows tensorMap to handle the Tensor -> scalar conversion.+                  if TVar v == elemType+                  then Right (emptySubst, True)+                  else if v `Set.member` freeTyVars elemType+                       then Left $ OccursCheck v elemType+                       else Right (singletonSubst v elemType, True)       _ ->-        -- Non-Tensor type, regular occurs check-        if v `Set.member` freeTyVars t-        then Left $ OccursCheck v t-        else Right (singletonSubst v t, False)+        fmap (\s -> (s, False)) $ occursCheckAndBind v t --- | Check if there's an instance for Constraint (Tensor elemType)--- e.g., check if Num (Tensor Integer) exists given elemType = Integer and constraint = Num+-- | Occurs check and variable binding (shared helper).+occursCheckAndBind :: TyVar -> Type -> Either UnifyError Subst+occursCheckAndBind v t+  | v `Set.member` freeTyVars t = Left $ OccursCheck v t+  | otherwise = Right $ singletonSubst v t++--------------------------------------------------------------------------------+-- Tensor-Specific Helpers (ConstraintAware mode only)+--------------------------------------------------------------------------------++-- | Unify Tensor elemType with a non-Tensor type, considering constraints.+unifyTensorWithConstraints :: ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError (Subst, Bool)+unifyTensorWithConstraints classEnv constraints elemType otherType =+  case otherType of+    TVar v ->+      unifyVarConstraintAware classEnv constraints v (TTensor elemType)+    _ -> do+      (s, _) <- unifyNormalized TensorConstraintAware classEnv constraints elemType otherType+      Right (s, True)++-- | Check if there's an instance for Constraint (Tensor elemType). hasInstanceForTensorType :: ClassEnv -> Type -> Constraint -> Bool hasInstanceForTensorType classEnv elemType (Constraint className _) =   let tensorType = TTensor elemType@@ -470,69 +405,182 @@                      Left _  -> False          ) instances --- | Unify Tensor elemType with a non-Tensor type, considering constraints--- Returns (Subst, Bool) where Bool indicates if Tensor was unwrapped during unification-unifyTensorWithConstraints :: ClassEnv -> [Constraint] -> Type -> Type -> Either UnifyError (Subst, Bool)-unifyTensorWithConstraints classEnv constraints elemType otherType =-  case otherType of-    TVar v ->-      -- Symmetric case: handled by unifyVarWithConstraints-      unifyVarWithConstraints classEnv constraints v (TTensor elemType)-    _ ->-      -- Normal unification: Tensor elemType with otherType means elemType = otherType-      -- FLAG: Set to True because we're unwrapping Tensor-      do-        (s, _) <- unifyWithConstraints classEnv constraints elemType otherType-        Right (s, True)---- | Unify multiple type pairs with constraints--- Returns (Subst, Bool) where Bool indicates if any Tensor was unwrapped during unification-unifyManyWithConstraints :: ClassEnv -> [Constraint] -> [Type] -> [Type] -> Either UnifyError (Subst, Bool)-unifyManyWithConstraints _ _ [] [] = Right (emptySubst, False)-unifyManyWithConstraints classEnv constraints (t1:ts1) (t2:ts2) = do-  (s1, flag1) <- unifyWithConstraints classEnv constraints t1 t2-  let constraints' = map (applySubstConstraint s1) constraints-  (s2, flag2) <- unifyManyWithConstraints classEnv constraints' (map (applySubst s1) ts1) (map (applySubst s1) ts2)-  Right (composeSubst s2 s1, flag1 || flag2)-unifyManyWithConstraints _ _ _ _ = Left $ TypeMismatch (TTuple []) (TTuple [])+--------------------------------------------------------------------------------+-- Matcher-Tuple Unification (ConstraintAware mode only)+-------------------------------------------------------------------------------- --- | Unify Matcher b with (t1, t2, ...) using constraint-aware unification--- Result: b = (c1, c2, ...) where ti unifies with Matcher ci--- Returns (Subst, Bool) where Bool indicates if any Tensor was unwrapped during unification-unifyMatcherWithTupleWithConstraints :: ClassEnv -> [Constraint] -> Type -> [Type] -> Either UnifyError (Subst, Bool)-unifyMatcherWithTupleWithConstraints classEnv constraints b ts = do-  -- Process each element: extract inner type or create constraint-  (innerTypes, s1, flag1) <- unifyEachAsMatcherWithConstraints classEnv constraints ts emptySubst-  -- Now unify b with (c1, c2, ...)+-- | Unify Matcher b with (t1, t2, ...) by treating each ti as Matcher ci.+-- Result: b = (c1, c2, ...) where ti unifies with Matcher ci.+unifyMatcherWithTupleG :: ClassEnv -> [Constraint] -> Type -> [Type] -> Either UnifyError (Subst, Bool)+unifyMatcherWithTupleG classEnv constraints b ts = do+  (innerTypes, s1, flag1) <- unifyEachAsMatcher classEnv constraints ts emptySubst   let tupleType = TTuple innerTypes       constraints' = map (applySubstConstraint s1) constraints-  (s2, flag2) <- unifyWithConstraints classEnv constraints' (applySubst s1 b) tupleType+  (s2, flag2) <- unifyNormalized TensorConstraintAware classEnv constraints' (applySubst s1 b) tupleType   Right (composeSubst s2 s1, flag1 || flag2)++-- | Treat each element of a tuple as a matcher and extract its inner type,+-- threading a substitution.  Result: the list of inner types c1..ck such that+-- each ti unifies with @Matcher ci@.  Shared by 'unifyMatcherWithTupleG' (Matcher+-- side) and 'coerceSlotTuple' (MatcherSlot side).+unifyEachAsMatcher :: ClassEnv -> [Constraint] -> [Type] -> Subst -> Either UnifyError ([Type], Subst, Bool)+unifyEachAsMatcher _ _ [] s = Right ([], s, False)+unifyEachAsMatcher env cons (t:rest) s = do+  let t' = applySubst s t+      cons' = map (applySubstConstraint s) cons+  (innerType, s1, flag1) <- case t' of+    TMatcher inner -> Right (inner, emptySubst, False)+    -- A MatcherSlot element (e.g. a slot-typed parameter used in a next-matcher+    -- tuple like @(m, list m)@): its target component is its inner type.+    TMatcherSlot _ tt -> Right (tt, emptySubst, False)+    TVar v -> do+      let innerVar = TyVar (getTyVarName v ++ "'")+          innerTy = TVar innerVar+      (s', flag) <- unifyNormalized TensorConstraintAware env cons' t' (TMatcher innerTy)+      Right (applySubst s' innerTy, s', flag)+    _ -> Left $ TypeMismatch (TMatcher (TVar (TyVar "?"))) t'++  let s2 = composeSubst s1 s+      cons'' = map (applySubstConstraint s2) cons+  (restInnerTypes, s3, flag2) <- unifyEachAsMatcher env cons'' rest s2+  Right (applySubst s3 innerType : restInnerTypes, s3, flag1 || flag2)++getTyVarName :: TyVar -> String+getTyVarName (TyVar name) = name++--------------------------------------------------------------------------------+-- COERCE-MATCHER-TO-SLOT (paper: one-way Matcher -> MatcherSlot coercion)+--------------------------------------------------------------------------------++-- | Coerce a Matcher value of intrinsic type @tm@ (paper τ_m) into the slot @MatcherSlot tp tt@+-- (paper @MatcherSlot τ_p τ_t@: structural index @tp@ = τ_p, target index @tt@ = τ_t).+-- Dual check (paper COERCE-MATCHER-TO-SLOT):+--   (1) structural admissibility (τ_m ⊑ τ_p): @tp@ can be specialized (one-way, binding only+--       @tp@'s variables) to @tm@.  Checked FIRST, on the intrinsic @tm@, so that e.g.+--       @something : Matcher a@ is rejected at a constructor-headed slot (its @a@ has+--       not yet been concretized by the target unification).+--   (2) target unifiability (τ_m ~ τ_t): @tm ~ tt@.+-- The paper freezes the matcher with a fresh renaming @τ_m' = fresh_rename(τ_m)@ (used only in+-- the structural premise @τ_m' ⊑ τ_p@, the target premise keeping the original @τ_m@) purely to+-- make the two premises evaluable in any order.  We do not rename: 'matchOneWay' treats @tm@ as+-- rigid (binds only @tp@'s variables), and fixing the order — structural check FIRST, on the+-- un-substituted @tm@ — already prevents the target unification from leaking into the structural+-- check.  So the fresh copy is unnecessary; both realize the identical admissibility predicate.+-- (This is also why no @fresh_rename@ is applied to the structural index @tp@ (τ_p) at a match+-- site: paper WT-ATOM/T-MATCH.)+coerceMatcherToSlot :: TensorHandling -> ClassEnv -> [Constraint] -> Type -> Type -> Type+                    -> Either UnifyError (Subst, Bool)+coerceMatcherToSlot mode ce cs tm tp tt =+  case matchOneWay tp tm of+    Nothing   -> Left $ TypeMismatch (TMatcher tm) (TMatcherSlot tp tt)+    Just subS -> do+      let cs' = map (applySubstConstraint subS) cs+      (subT, flagT) <- unifyNormalized mode ce cs' (applySubst subS tm) (applySubst subS tt)+      Right (composeSubst subT subS, flagT)++-- | COERCE-SLOT-TUPLE: a tuple of matchers @(m1, ..., mk)@ filling a product slot+-- @MatcherSlot tp tt@ (structural index @tp@ = τ_p, target index @tt@ = τ_t).+--+-- When the slot's structural and target indices are themselves @k@-tuples, decompose the+-- product slot into component slots and check each tuple-matcher component against its own+-- @MatcherSlot σ_i τ_i@ (the paper's COERCE-SLOT-TUPLE).  This *defers* a component that is a+-- matcher parameter (committing it to a component slot) rather than folding it into a bare+-- @Matcher@ — so e.g. @\\m -> matchAll (xs, n) as (m, integer) with ($x :: $xs, $n) -> ...@+-- commits @m@ to a list-headed component slot instead of rejecting it, while still rejecting+-- @something@ there.+--+-- Otherwise (a variable-headed slot, or a non-tuple target) fold the tuple of matchers into a+-- single product @Matcher@ and apply the standard COERCE-MATCHER-TO-SLOT dual check.  This is+-- what lets a matcher constructor whose element parameter is a slot (e.g.+-- @list (m : MatcherSlot a a)@) still accept a tuple matcher such as @(m, integer)@.+coerceSlotTuple :: TensorHandling -> ClassEnv -> [Constraint] -> Type -> Type -> [Type]+                -> Either UnifyError (Subst, Bool)+coerceSlotTuple mode ce cs tp tt tys+  | TTuple sigmas <- tp, TTuple taus <- tt+  , length sigmas == length tys, length taus == length tys =+      goComponents (zip3 tys sigmas taus) emptySubst False+  | otherwise = do+      (innerTypes, s1, flag1) <- unifyEachAsMatcher ce cs tys emptySubst+      let tm  = TTuple innerTypes+          cs' = map (applySubstConstraint s1) cs+      (s2, flag2) <- coerceMatcherToSlot mode ce cs'+                       (applySubst s1 tm) (applySubst s1 tp) (applySubst s1 tt)+      Right (composeSubst s2 s1, flag1 || flag2)   where-    -- Unify each type in the tuple with Matcher ci, extracting ci-    unifyEachAsMatcherWithConstraints :: ClassEnv -> [Constraint] -> [Type] -> Subst -> Either UnifyError ([Type], Subst, Bool)-    unifyEachAsMatcherWithConstraints _ _ [] s = Right ([], s, False)-    unifyEachAsMatcherWithConstraints env cons (t:rest) s = do-      let t' = applySubst s t-          cons' = map (applySubstConstraint s) cons-      (innerType, s1, flag1) <- case t' of-        -- If already Matcher c, extract c-        TMatcher inner -> Right (inner, emptySubst, False)-        -- If type variable, unify it with Matcher (fresh variable)-        TVar v -> do-          -- Generate a new variable name for the inner type-          let innerVar = TyVar (getTyVarName v ++ "'")-              innerType = TVar innerVar-          (s', flag) <- unifyWithConstraints env cons' t' (TMatcher innerType)-          Right (applySubst s' innerType, s', flag)-        -- Other types cannot be unified with Matcher-        _ -> Left $ TypeMismatch (TMatcher (TVar (TyVar "?"))) t'+    goComponents [] acc flag = Right (acc, flag)+    goComponents ((ty, sigma, tau) : rest) acc flag = do+      let cs' = map (applySubstConstraint acc) cs+      (s', f') <- unifyNormalized mode ce cs'+                    (applySubst acc ty)+                    (TMatcherSlot (applySubst acc sigma) (applySubst acc tau))+      goComponents rest (composeSubst s' acc) (flag || f') -      let s2 = composeSubst s1 s-          cons'' = map (applySubstConstraint s2) cons-      (restInnerTypes, s3, flag2) <- unifyEachAsMatcherWithConstraints env cons'' rest s2-      Right (applySubst s3 innerType : restInnerTypes, s3, flag1 || flag2)+-- | One-way matching: is there a substitution over @slot@'s type variables making+-- @slot == matcher@, with @matcher@ rigid (its variables are never bound)?+-- A variable-headed @slot@ admits any matcher (bind the variable); a constructor- or+-- concrete-headed @slot@ rejects a bare-variable matcher (e.g. @something@). Repeated+-- slot variables are matched consistently (resolved via the accumulated substitution).+matchOneWay :: Type -> Type -> Maybe Subst+matchOneWay slot0 matcher0 = go [(slot0, matcher0)] emptySubst+  where+    go [] acc = Just acc+    go ((s, t) : rest) acc =+      case applySubst acc s of+        TVar v -> go rest (composeSubst (singletonSubst v t) acc)+        s'     -> matchStruct s' t rest acc+    matchStruct (TCollection a) (TCollection b) rest acc = go ((a, b) : rest) acc+    matchStruct (TTuple as) (TTuple bs) rest acc+      | length as == length bs = go (zip as bs ++ rest) acc+    matchStruct (TInductive n as) (TInductive m bs) rest acc+      | n == m && length as == length bs = go (zip as bs ++ rest) acc+    matchStruct (TTensor a) (TTensor b) rest acc = go ((a, b) : rest) acc+    matchStruct (THash k1 v1) (THash k2 v2) rest acc = go ((k1, k2) : (v1, v2) : rest) acc+    matchStruct (TFun a1 r1) (TFun a2 r2) rest acc = go ((a1, a2) : (r1, r2) : rest) acc+    matchStruct (TMatcher a) (TMatcher b) rest acc = go ((a, b) : rest) acc+    matchStruct (TMatcherSlot s1 t1) (TMatcherSlot s2 t2) rest acc = go ((s1, s2) : (t1, t2) : rest) acc+    matchStruct (TIO a) (TIO b) rest acc = go ((a, b) : rest) acc+    matchStruct (TIORef a) (TIORef b) rest acc = go ((a, b) : rest) acc+    matchStruct a b rest acc+      | a == b          = go rest acc   -- base types match exactly+      | groundEquiv a b = go rest acc   -- CAS ground equivalence (Integer ~ MathValue ~ Factor/Term/Frac/Poly)+      | otherwise       = Nothing -    getTyVarName :: TyVar -> String-    getTyVarName (TyVar name) = name+-- | CAS ground-type equivalence: the closed, ClassEnv-free subtype/widening+-- rules of 'unifyG' (Integer, MathValue, Factor, Term, Frac, Poly are mutually+-- equivalent at the ground level).  This lets 'matchOneWay' admit a concrete CAS+-- matcher at a concrete CAS slot — e.g. @integer : Matcher Integer@ filling the+-- @MatcherSlot MathValue MathValue@ that the body of @term@/@poly@/@frac@ pins.+groundEquiv :: Type -> Type -> Bool+groundEquiv a b = isCASGround a && isCASGround b+  where+    isCASGround TInt        = True+    isCASGround TMathValue  = True+    isCASGround TFactor     = True+    isCASGround (TTerm _ _) = True+    isCASGround (TFrac _)   = True+    isCASGround (TPoly _ _) = True+    isCASGround _           = False +--------------------------------------------------------------------------------+-- CAS Symbol Set Unification+--------------------------------------------------------------------------------++-- | Unify two symbol sets, returning the unified symbol set if compatible.+-- Rules:+--   - Open [..] unifies with anything, resulting in the more specific one+--   - Closed [x, y] unifies with Closed [x, y] if they're equal (or one is subset)+--   - SymbolSetVar can unify with concrete symbol sets+unifySymbolSets :: SymbolSet -> SymbolSet -> Maybe SymbolSet+unifySymbolSets SymbolSetOpen ss = Just ss+unifySymbolSets ss SymbolSetOpen = Just ss+unifySymbolSets (SymbolSetClosed s1) (SymbolSetClosed s2)+  | s1 == s2 = Just (SymbolSetClosed s1)+  -- Subset checking: unify to the larger set+  | all (`elem` s2) s1 = Just (SymbolSetClosed s2)  -- s1 ⊆ s2+  | all (`elem` s1) s2 = Just (SymbolSetClosed s1)  -- s2 ⊆ s1+  | otherwise = Nothing  -- No subset relationship+unifySymbolSets (SymbolSetVar v1) (SymbolSetVar v2)+  | v1 == v2 = Just (SymbolSetVar v1)+  | otherwise = Just (SymbolSetVar v1)  -- Arbitrary choice; needs substitution tracking+unifySymbolSets (SymbolSetVar _) ss = Just ss+unifySymbolSets ss (SymbolSetVar _) = Just ss
lib/core/assoc.egi view
@@ -20,7 +20,7 @@ -- Assoc Multiset -- -def assocMultiset {a} (m: Matcher a) : Matcher [(a, Integer)] :=+def assocMultiset {a} (m: MatcherSlot a a) : Matcher [(a, Integer)] :=   matcher     | [] as () with       | [] -> [()]@@ -43,11 +43,13 @@       | $tgt ->         matchAll tgt as list (m, integer) with           | $hs ++ ($x, $n) :: $ts -> (x, n, hs ++ ts)-    | #$x :: $ as (assocMultiset m) with-      | $tgt ->-        matchAll tgt as list (m, integer) with-          | $hs ++ (#x, $n) :: $ts ->-            if n = 1 then hs ++ ts else hs ++ (x, n - 1) :: ts+    -- NOTE: the untyped Egison version also had an element-view clause+    --   | #$x :: $ as (assocMultiset m) with ...+    -- binding the head VALUE at the element type `a` (decrement-one-occurrence+    -- semantics).  Under the typed pattern-constructor rules the head of `::`+    -- at the matched type [(a, Integer)] is the PAIR (a, Integer) — for the+    -- clause's capture and for the user's value pattern alike — so that view+    -- is not typeable; use the pair view `(#v, $n) :: rest` instead.     | $ as (something) with       | $tgt -> [tgt] @@ -55,6 +57,6 @@   matchAll (xs, ys) as (assocMultiset something, assocMultiset something) with     | (($x, $m) :: _, (#x, $n) :: _) -> (x, min m n) -def AC.intersectAs (m : Matcher a) (xs : [(a, Integer)]) (ys : [(a, Integer)]) : [(a, Integer)] :=+def AC.intersectAs (m : MatcherSlot a a) (xs : [(a, Integer)]) (ys : [(a, Integer)]) : [(a, Integer)] :=   matchAll (xs, ys) as (assocMultiset m, assocMultiset m) with     | (($x, $m) :: _, (#x, $n) :: _) -> (x, min m n)
lib/core/base.egi view
@@ -41,35 +41,167 @@     | $ as something with       | $tgt -> [tgt] -def bool : Matcher Bool := eq-def char : Matcher Char := eq-def integer : Matcher Integer := eq-def float : Matcher Float := eq+-- The primitive matchers are eq's body inlined at each concrete type.+-- Matcher types are rigid (two different Matcher types never unify), so a+-- polymorphic matcher value cannot be specialized by annotation: `def+-- integer : Matcher Integer := eq` does not type-check, and an unannotated+-- alias `def integer := eq` would keep eq's polymorphic scheme, which a+-- concrete hole (e.g. `as (integer, ...)` at an Integer hole) rejects just+-- as it rejects `something`.  A concrete matcher literal instead derives+-- its capability at the annotated type (T-MATCHER in checking mode).+-- The #$val arm uses the built-in equality `=` (same convention as+-- sortedList's #$val arm); for the base types it coincides with ==. -class Num a where+def bool : Matcher Bool :=+  matcher+    | #$val as () with+      | $tgt -> if val = tgt then [()] else []+    | $ as something with+      | $tgt -> [tgt]++def char : Matcher Char :=+  matcher+    | #$val as () with+      | $tgt -> if val = tgt then [()] else []+    | $ as something with+      | $tgt -> [tgt]++def integer : Matcher Integer :=+  matcher+    | #$val as () with+      | $tgt -> if val = tgt then [()] else []+    | $ as something with+      | $tgt -> [tgt]++def float : Matcher Float :=+  matcher+    | #$val as () with+      | $tgt -> if val = tgt then [()] else []+    | $ as something with+      | $tgt -> [tgt]++-- Additive hierarchy+class AddSemigroup a where   (+) (x: a) (y: a) : a-  (-) (x: a) (y: a) : a++class AddMonoid a extends AddSemigroup a where+  zero : a++class AddGroup a extends AddMonoid a where+  neg (x: a) : a++-- Multiplicative hierarchy+class MulSemigroup a where   (*) (x: a) (y: a) : a-  (/) (x: a) (y: a) : a ---instance Num Integer where---  (+) x y := i.+ x y---  (-) x y := i.- x y---  (*) x y := i.* x y---  (/) x y := i./ x y-instance Num MathExpr where-  (+) x y := plusForMathExpr x y-  (-) x y := minusForMathExpr x y-  (*) x y := multForMathExpr x y-  (/) x y := divForMathExpr x y+class MulMonoid a extends MulSemigroup a where+  one : a -instance Num Float where+class MulGroup a extends MulMonoid a where+  inv (x: a) : a++-- Composite structures+class Ring a extends AddGroup a, MulMonoid a+class Field a extends Ring a, MulGroup a++class GCDDomain a extends Ring a where+  gcd (x: a) (y: a) : a++class EuclideanDomain a extends GCDDomain a where+  divMod (x: a) (y: a) : (a, a)++-- Derived operators+def (-) {AddGroup a} (x: a) (y: a) : a := x + neg y+def (/) {Field a} (x: a) (y: a) : a := x * inv y+def modulo {EuclideanDomain a} (x: a) (y: a) : a := snd (divMod x y)+def quotient {EuclideanDomain a} (x: a) (y: a) : a := fst (divMod x y)++-- MathValue instances+instance AddSemigroup MathValue where+  (+) x y := plusForMathValue x y++instance AddMonoid MathValue where+  zero := 0++instance AddGroup MathValue where+  neg x := minusForMathValue 0 x++instance MulSemigroup MathValue where+  (*) x y := multForMathValue x y++instance MulMonoid MathValue where+  one := 1++instance MulGroup MathValue where+  inv x := divForMathValue 1 x++instance Ring MathValue+instance Field MathValue++instance GCDDomain MathValue where+  gcd x y := gcdForMathValue x y++instance EuclideanDomain MathValue where+  divMod x y := (i.quotient x y, i.modulo x y)++-- Float instances+instance AddSemigroup Float where   (+) x y := f.+ x y-  (-) x y := f.- x y++instance AddMonoid Float where+  zero := 0.0++instance AddGroup Float where+  neg x := f.- 0.0 x++instance MulSemigroup Float where   (*) x y := f.* x y-  (/) x y := f./ x y +instance MulMonoid Float where+  one := 1.0++instance MulGroup Float where+  inv x := f./ 1.0 x++instance Ring Float+instance Field Float+ --+-- CAS type widening / narrowing+--+-- After Phase C, both `Embed` and the `coerceTo*` helper functions have been+-- removed in favor of the unified `reshape` mechanism: the type checker+-- inserts a `TIReshape T` node whenever a `def x : T := e` annotation is+-- encountered (where T is a concrete CAS scalar — Integer, Factor, Frac _,+-- Poly _ _, Term _ _). At runtime, `casReshapeAs T v` structurally rewrites+-- the CAS value to fit T (collapsing Frac with denom=1 to Integer, lifting+-- Integer into Poly form, etc.).+--+-- Subtype unification handles the static side (Integer ⊂ Frac Integer ⊂+-- Poly Integer [..] ⊂ Poly (Frac Integer) [..] ⊂ Frac (Poly Integer [..])).+-- Together this gives "trust the annotation" semantics:+--+--   def n : Integer            := someExpr   -- reshapes runtime to Integer+--   def p : Poly Integer [x,y] := someExpr   -- reshapes to specific atom set+--   def t : Term MathValue [..]:= someExpr   -- reshapes to single-term form+--++--+-- Rule application combinators (Phase 7.5 helper)+--+-- `applyRules` runs each rule once, left-to-right. `iterateRules` runs+-- the chain repeatedly until the value stops changing (fixed point).+-- Users hand-build the rule list from `rule.<name>` references.+--++def applyRules {a} (rules : [a -> a]) (v : a) : a :=+  foldl (\acc r -> r acc) v rules++def iterateRules {Eq a} (rules : [a -> a]) (v : a) : a :=+  let v' := applyRules rules v+   in if v == v' then v else iterateRules rules v'++-- -- Utility -- @@ -84,7 +216,7 @@  def flip {a, b, c} (fn: a -> b -> c) : b -> a -> c := \x y -> fn y x -def eqAs {Eq a} (m: Matcher a) (x: a) (y: a) : Bool :=+def eqAs {a} (m: MatcherSlot a a) (x: a) (y: a) : Bool :=   match x as m with     | #y -> True     | _ -> False@@ -105,7 +237,7 @@ -- Unordered Pair -- -def unorderedPair {a} (m: Matcher a) : Matcher (a, a) :=+def unorderedPair {a} (m: MatcherSlot a a) : Matcher (a, a) :=   matcher     | ($, $) as (m, m) with       | ($x, $y) -> [(x, y), (y, x)]
lib/core/collection.egi view
@@ -13,7 +13,7 @@ -- -- List ---def list {a} (m: Matcher a) : Matcher [a] :=+def list {a} (m: MatcherSlot a a) : Matcher [a] :=   matcher     | [] as () with       | [] -> [()]@@ -43,7 +43,7 @@     | $ as (something) with       | $tgt -> [tgt] -def sortedList {Ord a} (m: Matcher a) : Matcher [a] :=+def sortedList {Ord a} (m: MatcherSlot a a) : Matcher [a] :=   matcher     | [] as () with       | [] -> [()]@@ -304,7 +304,7 @@     | $xs ++ #sep ++ $rs -> xs :: split sep rs     | _ -> [ls] -def splitAs {a} (m: Matcher a) (sep: [a]) (ls: [a]) : [[a]] :=+def splitAs {a} (m: MatcherSlot a a) (sep: [a]) (ls: [a]) : [[a]] :=   match ls as list m with     | $xs ++ #sep ++ $rs -> xs :: splitAs m sep rs     | _ -> [ls]@@ -344,7 +344,7 @@ -- -- Multiset ---def multiset {a} (m: Matcher a) : Matcher [a] :=+def multiset {a} (m: MatcherSlot a a) : Matcher [a] :=   matcher     | [] as () with       | [] -> [()]@@ -387,7 +387,7 @@     | #x :: $rs -> rs     | $y :: $rs -> y :: deleteFirst x rs -def deleteFirstAs {a} (m: Matcher a) (x: a) (xs: [a]) : [a] :=+def deleteFirstAs {a} (m: MatcherSlot a a) (x: a) (xs: [a]) : [a] :=   match xs as list m with     | [] -> []     | #x :: $rs -> rs@@ -399,7 +399,7 @@     | $hs ++ #x :: $ts -> hs ++ delete x ts     | _ -> xs -def deleteAs {a} (m: Matcher a) (x: a) (xs: [a]) : [a] :=+def deleteAs {a} (m: MatcherSlot a a) (x: a) (xs: [a]) : [a] :=   match xs as list m with     | [] -> []     | $hs ++ #x :: $ts -> hs ++ deleteAs m x ts@@ -410,7 +410,7 @@     | [] -> xs     | $y :: $rs -> difference (deleteFirst y xs) rs -def differenceAs {a} (m: Matcher a) (xs: [a]) (ys: [a]) : [a] :=+def differenceAs {a} (m: MatcherSlot a a) (xs: [a]) (ys: [a]) : [a] :=   match ys as list m with     | [] -> xs     | $y :: $rs -> differenceAs m (deleteFirstAs m y xs) rs@@ -421,17 +421,17 @@     | $y :: $rs ->       if member y xs then include (deleteFirst y xs) rs else False -def includeAs {a} (m: Matcher a) (xs: [a]) (ys: [a]) : Bool :=+def includeAs {a} (m: MatcherSlot a a) (xs: [a]) (ys: [a]) : Bool :=   match ys as list m with     | [] -> True     | $y :: $rs ->-      if memberAs m y xs then includeAs m (deleteFirst y xs) rs else False+      if memberAs m y xs then includeAs m (deleteFirstAs m y xs) rs else False  def union {Eq a} (xs: [a]) (ys: [a]) : [a] :=   xs ++ (matchAll (ys, xs) as (multiset something, multiset something) with     | ($y :: _, !(#y :: _)) -> y) -def unionAs {a} (m: Matcher a) (xs: [a]) (ys: [a]) : [a] :=+def unionAs {a} (m: MatcherSlot a a) (xs: [a]) (ys: [a]) : [a] :=   xs ++ (matchAll (ys, xs) as (multiset m, multiset m) with     | ($y :: _, !(#y :: _)) -> y) @@ -439,7 +439,7 @@   matchAll (xs, ys) as (multiset something, multiset something) with     | ($x :: _, #x :: _) -> x -def intersectAs {a} (m: Matcher a) (xs: [a]) (ys: [a]) : [a] :=+def intersectAs {a} (m: MatcherSlot a a) (xs: [a]) (ys: [a]) : [a] :=   matchAll (xs, ys) as (multiset m, multiset m) with     | ($x :: _, #x :: _) -> x @@ -451,7 +451,7 @@     | _ ++ #x :: _ -> True     | _ -> False -def memberAs {a} (m: Matcher a) (x: a) (ys: [a]) : Bool :=+def memberAs {a} (m: MatcherSlot a a) (x: a) (ys: [a]) : Bool :=   match ys as list m with     | _ ++ #x :: _ -> True     | _ -> False@@ -462,13 +462,13 @@ def count {Eq a} (x: a) (xs: [a]) : Integer :=   foldl (\acc y -> if x = y then acc + 1 else acc) 0 xs -def countAs {a} (m: Matcher a) (x: a) (xs: [a]) : Integer :=+def countAs {a} (m: MatcherSlot a a) (x: a) (xs: [a]) : Integer :=   foldl (\acc y -> if eqAs m x y then acc + 1 else acc) 0 xs  def frequency {Eq a} (xs: [a]) : [(a, Integer)] :=   map (\u -> (u, count u xs)) (unique xs) -def frequencyAs {a} (m: Matcher a) (xs: [a]) : [(a, Integer)] :=+def frequencyAs {a} (m: MatcherSlot a a) (xs: [a]) : [(a, Integer)] :=   map (\u -> (u, countAs m u xs)) (uniqueAs m xs)  --@@ -478,10 +478,23 @@   matchAll xs as list something with     | $hs ++ #x :: _ -> 1 + length hs +def unique {Eq a} (xs: [a]) : [a] :=+  reverse+    (matchAll reverse xs as list something with+      | _ ++ $x :: !(_ ++ #x :: _) -> x)++def uniqueAs {a} (m: MatcherSlot a a) (xs: [a]) : [a] := loopFn xs []+  where+    loopFn (xs: [a]) (ys: [a]) : [a] :=+      match (xs, ys) as (list m, multiset m) with+        | ([], _) -> ys+        | ($x :: $rs, #x :: _) -> loopFn rs ys+        | ($x :: $rs, _) -> loopFn rs (ys ++ [x])+ -- -- Set ---def set {a} (m: Matcher a) : Matcher [a] :=+def set {a} (m: MatcherSlot a a) : Matcher [a] :=   matcher     | [] as () with       | [] -> [()]@@ -504,7 +517,7 @@               $ts -> (map (\i -> x_i) [1..n], tgt)     | #$val as () with       | $tgt ->-        match (unique val, unique tgt) as (list m, multiset m) with+        match (uniqueAs m val, uniqueAs m tgt) as (list m, multiset m) with           | ([], []) -> [()]           | ($x :: $xs, #x :: #xs) -> [()]           | (_, _) -> []@@ -516,21 +529,8 @@ -- def add {Eq a} (x: a) (xs: [a]) : [a] := if member x xs then xs else xs ++ [x] -def addAs {a} (m: Matcher a) (x: a) (xs: [a]) : [a] := if memberAs m x xs then xs else xs ++ [x]+def addAs {a} (m: MatcherSlot a a) (x: a) (xs: [a]) : [a] := if memberAs m x xs then xs else xs ++ [x] -def fastUnique {Eq a} (xs: [a]) : [a] :=+def fastUnique {Ord a} (xs: [a]) : [a] :=   matchAll sort xs as list something with     | _ ++ $x :: !(#x :: _) -> x--def unique {Eq a} (xs: [a]) : [a] :=-  reverse-    (matchAll reverse xs as list something with-      | _ ++ $x :: !(_ ++ #x :: _) -> x)--def uniqueAs {a} (m: Matcher a) (xs: [a]) : [a] := loopFn xs []-  where-    loopFn (xs: [a]) (ys: [a]) : [a] :=-      match (xs, ys) as (list m, multiset m) with-        | ([], _) -> ys-        | ($x :: $rs, #x :: _) -> loopFn rs ys-        | ($x :: $rs, _) -> loopFn rs (ys ++ [x])
lib/core/deprecated.egi view
@@ -32,11 +32,11 @@ -- -- Eigenvalues and eigenvectors ---def M.eigenvalues {Num a} (m: Matrix a) : [a] :=+def M.eigenvalues {Ring a} (m: Matrix a) : [a] :=   let (e1, e2) := qF (M.det (T.- m (scalarToTensor x [2, 2]))) x    in [e1, e2] -def M.eigenvectors {Num a} (m: Matrix a) : [(a, Vector a)] :=+def M.eigenvectors {Ring a} (m: Matrix a) : [(a, Vector a)] :=   let (e1, e2) := qF (M.det (T.- m (scalarToTensor x [2, 2]))) x    in [ (e1, clearIndex (T.- m (scalarToTensor e1 [2, 2]))_i_1)       , (e2, clearIndex (T.- m (scalarToTensor e2 [2, 2]))_i_1) ]@@ -44,7 +44,7 @@ -- -- LU decomposition ---def M.LU {Num a} (x: Matrix a) : (Matrix a, Matrix a) :=+def M.LU {Field a} (x: Matrix a) : (Matrix a, Matrix a) :=   match tensorShape x as list integer with     | [#2, #2] ->       let L := generateTensor
lib/core/maybe.egi view
@@ -12,7 +12,7 @@   | Nothing   | Just a -def maybe {a} (m: Matcher a) : Matcher (Maybe a) :=+def maybe {a} (m: MatcherSlot a a) : Matcher (Maybe a) :=   matcher     | nothing as () with       | Nothing -> [()]
lib/core/order.egi view
@@ -17,9 +17,6 @@     | equal     | greater -def min {Ord a} (x: a) (y: a) : a := if x < y then x else y-def max {Ord a} (x: a) (y: a) : a := if x > y then x else y- class Ord a extends Eq a where   compare (x: a) (y: a) : Ordering   (<) (x: a) (y: a) : Bool
lib/core/random.egi view
@@ -19,7 +19,7 @@  def R.between (s: Integer) (e: Integer) : [Integer] := randomize [s..e] -def R.multiset {a} (m: Matcher a) : Matcher [a] :=+def R.multiset {a} (m: MatcherSlot a a) : Matcher [a] :=   matcher     | [] as () with       | [] -> [()]@@ -54,7 +54,7 @@  def sample {a} : [a] -> a := R.head -def R.set {a} (m: Matcher a) : Matcher [a] :=+def R.set {a} (m: MatcherSlot a a) : Matcher [a] :=   matcher     | [] as () with       | [] -> [()]
lib/math/algebra/equations.egi view
@@ -4,12 +4,12 @@ -- -- ---def solve (eqs: [(MathExpr, MathExpr, MathExpr)]) : [(MathExpr, MathExpr)] := solve' eqs []+--def solve (eqs: [(MathValue, MathValue, MathValue)]) : [(MathValue, MathValue)] := solve' eqs [] --  where---    solve1 (f: MathExpr) (expr: MathExpr) (x: MathExpr) : MathExpr := inverse expr f x+--    solve1 (f: MathValue) (expr: MathValue) (x: MathValue) : MathValue := inverse expr f x -----    solve' (eqs: [(MathExpr, MathExpr, MathExpr)]) (rets: [(MathExpr, MathExpr)]) : [(MathExpr, MathExpr)] :=---      match eqs as list (mathExpr, mathExpr, mathExpr) with+--    solve' (eqs: [(MathValue, MathValue, MathValue)]) (rets: [(MathValue, MathValue)]) : [(MathValue, MathValue)] :=+--      match eqs as list (mathValue, mathValue, mathValue) with --        | [] -> rets --        | ($f, $expr, $x) :: $rs -> --          solve'@@ -19,27 +19,27 @@ -- -- Quadratic Equations ---def quadraticFormula : MathExpr -> MathExpr -> (MathExpr, MathExpr) := qF+def quadraticFormula : MathValue -> MathValue -> (MathValue, MathValue) := qF -def qF (f: MathExpr) (x: MathExpr) : (MathExpr, MathExpr) :=-  match coefficients f x as list mathExpr with+def qF (f: MathValue) (x: MathValue) : (MathValue, MathValue) :=+  match coefficients f x as list mathValue with     | [$a_0, $a_1, $a_2] -> qF' a_2 a_1 a_0 -def qF' (a: MathExpr) (b: MathExpr) (c: MathExpr) : (MathExpr, MathExpr) :=+def qF' (a: MathValue) (b: MathValue) (c: MathValue) : (MathValue, MathValue) :=   ( ((- b) + sqrt (b ^ 2 - 4 * a * c)) / 2 * a   , ((- b) - sqrt (b ^ 2 - 4 * a * c)) / 2 * a )  -- -- Cubic Equations ---def cubicFormula : MathExpr -> MathExpr -> (MathExpr, MathExpr, MathExpr) := cF+def cubicFormula : MathValue -> MathValue -> (MathValue, MathValue, MathValue) := cF -def cF (f: MathExpr) (x: MathExpr) : (MathExpr, MathExpr, MathExpr) :=-  match coefficients f x as list mathExpr with+def cF (f: MathValue) (x: MathValue) : (MathValue, MathValue, MathValue) :=+  match coefficients f x as list mathValue with     | $a_0 :: $a_1 :: $a_2 :: $a_3 :: [] -> cF' a_3 a_2 a_1 a_0 -def cF' (a: MathExpr) (b: MathExpr) (c: MathExpr) (d: MathExpr) : (MathExpr, MathExpr, MathExpr) :=-  match (a, b, c, d) as (mathExpr, mathExpr, mathExpr, mathExpr) with+def cF' (a: MathValue) (b: MathValue) (c: MathValue) (d: MathValue) : (MathValue, MathValue, MathValue) :=+  match (a, b, c, d) as (mathValue, mathValue, mathValue, mathValue) with     | (#1, #0, $p, $q) ->       let (s1, s2) := (2)#(rt 3 $1, rt 3 $2) (qF' 1 (27 * q) ((-27) * p ^ 3))        in ( (s1 + s2) / 3               -- r1
+ lib/math/algebra/groebner.egi view
@@ -0,0 +1,465 @@+--+--+-- Groebner Bases+--+--+-- Buchberger's algorithm and multivariate division (polynomial normal+-- forms) written in Egison itself, on top of the flat sum-of-products+-- representation.  Every factor of a term -- a symbol, a symbolic+-- application such as `'sin θ`, or a quoted expression -- is treated+-- as a variable, so ideals over compound atoms work without a change+-- of variables (design/cas-simplification.md, Section 3.4).+--+-- Monomial order: graded reverse lexicographic (grevlex).  The+-- variable priority list follows the declaration-order principle+-- (Section 3.5): atoms EARLIER in the list rank LOWER in the order+-- and therefore SURVIVE in normal forms.  `groebnerBasis` and+-- `polyNF` derive a default list from their arguments (atom-name+-- order); `groebnerBasisWith` / `polyNFWith` take the list+-- explicitly.+--+-- These functions are the value-level engine of the offline+-- rule-completion design (Section 3.3): a Groebner basis computed+-- once at declaration time turns into terminating, confluent+-- `declare rule auto term` rewrite rules.+--+-- Scope: generators and inputs must be polynomials with non-negative+-- exponents (no proper fractions, no Laurent monomials such as x^-1);+-- otherwise the functions return their input unchanged (fail-open,+-- as in the Haskell-side GCD reduction).++--+-- Atoms and the default variable order+--++-- All atoms (factors) appearing in a list of polynomials.+def polyAtoms (fs: [MathValue]) : [MathValue] :=+  unique+    (concat+      (map+        (\f -> matchAll f as mathValue with+          | poly (term _ (($x, _) :: _) :: _) -> x)+        fs))++def insertAtomByName (x: MathValue) (xs: [MathValue]) : [MathValue] :=+  match xs as list mathValue with+    | [] -> [x]+    | $y :: $ys ->+        if show x <= show y then x :: y :: ys else y :: insertAtomByName x ys++-- Deterministic fallback order: atom-name (dictionary) order.+def sortAtomsByName (xs: [MathValue]) : [MathValue] :=+  foldl (\acc x -> insertAtomByName x acc) [] xs++-- Priority list construction: the explicit prefix lists the atoms the+-- caller wants to survive (earlier = lower = kept); every remaining+-- atom of the inputs is appended in name order, ranking higher and+-- therefore getting eliminated first.  So `polyNFWith [sin θ] ...`+-- reads as "keep sin θ".+def extendAtoms (explicit: [MathValue]) (rest: [MathValue]) : [MathValue] :=+  explicit ++ sortAtomsByName (filter (\a -> not (member a explicit)) rest)++--+-- Monomials: exponent vectors and the grevlex order+--++def expOf (x: MathValue) (fs: [(MathValue, Integer)]) : Integer :=+  match fs as list (mathValue, integer) with+    | [] -> 0+    | (#x, $n) :: _ -> n+    | _ :: $rs -> expOf x rs++-- Exponent vector of a term, listed in priority order+-- (index 0 = the lowest, surviving atom).+def expVec (atoms: [MathValue]) (t: MathValue) : [Integer] :=+  match t as mathValue with+    | term _ $fs -> map (\a -> expOf a fs) atoms++def totalDegree (u: [Integer]) : Integer := foldl (\a b -> a + b) 0 u++-- u > v in grevlex: higher total degree wins; on ties, the vector+-- with the SMALLER exponent at the first differing position (= the+-- lowest-priority end of the vector) is the larger monomial.+def grevlexGreater (u: [Integer]) (v: [Integer]) : Bool :=+  if totalDegree u = totalDegree v+    then revlexGreater u v+    else totalDegree u > totalDegree v++def revlexGreater (us: [Integer]) (vs: [Integer]) : Bool :=+  match (us, vs) as (list integer, list integer) with+    | ($a :: $ars, $b :: $brs) ->+        if a = b then revlexGreater ars brs else a < b+    | (_, _) -> False++--+-- Leading terms, divisibility, lcm+--++def leadingTerm (atoms: [MathValue]) (f: MathValue) : MathValue :=+  match f as mathValue with+    | poly ($t :: $ts) ->+        foldl+          (\acc t' ->+            if grevlexGreater (expVec atoms t') (expVec atoms acc) then t' else acc)+          t+          ts++-- Does the monomial of s divide the monomial of t?  (Coefficients+-- are rationals, hence units; only exponents matter.)+def monoDivides (s: MathValue) (t: MathValue) : Bool :=+  match (s, t) as (mathValue, mathValue) with+    | (term _ $sfs, term _ $tfs) -> all (\(x, n) -> expOf x tfs >= n) sfs+    | (_, _) -> False++def monoLcm (s: MathValue) (t: MathValue) : MathValue :=+  match (s, t) as (mathValue, mathValue) with+    | (term _ $sfs, term _ $tfs) ->+        foldl+          (*')+          1+          (map+            (\x -> x ^' max (expOf x sfs) (expOf x tfs))+            (unique (map fst sfs ++ map fst tfs)))++--+-- Multivariate division (normal form)+--++-- One reduction step, as a single non-deterministic pattern match:+-- enumerate a basis element g and a term u of f such that the+-- leading term of g divides u, and eliminate u.+--+-- The whole engine computes in the FREE theory: all arithmetic uses+-- the rule-free structural operators (-', *', /'), so no `declare+-- rule` rewriting can fire during the computation.  This keeps the+-- division exact (the auto rules of the atoms, e.g. the built-in+-- Pythagorean rules, can neither collapse a generator inside a+-- product nor undo a reduction step), and it lets rule generation+-- (`declare ideal`) call the engine while the very rule being+-- generated is already registered, without re-entering itself.+-- Results re-enter the rule-aware world only at the public API+-- boundary: polyNF normalizes its result once.  Returns [] when f+-- is irreducible (in particular when f = 0).+def polyNFStep (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : [MathValue] :=+  take 1+    (matchAll (gs, f) as (multiset mathValue, mathValue) with+      | ($g :: _, poly ((?(monoDivides (leadingTerm atoms g)) & $u) :: _)) ->+          f -' ((u /' leadingTerm atoms g) *' g))++-- Reduce until no term is divisible.  Each step eliminates one+-- occurrence of a divisible monomial and only introduces strictly+-- smaller ones, so this terminates; the fuel is a cheap safety net.+-- The Integer in the result reports how the loop ended:+-- 0 = irreducible reached, 1 = fuel exhausted (result reduced but+-- possibly not a normal form).+def polyNFLoop (fuel: Integer) (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : (Integer, MathValue) :=+  if fuel = 0+    then (1, f)+    else match polyNFStep atoms gs f as list mathValue with+      | [] -> (0, f)+      | $f' :: _ -> polyNFLoop (fuel - 1) atoms gs f'++def polyNFEngine (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : MathValue :=+  snd (polyNFLoop 10000 atoms (filter (\g -> not (g = 0)) gs) f)++-- Guard: a polynomial with non-negative exponents (the value is not+-- a proper fraction, and no factor has a negative power).+def isGbPoly (f: MathValue) : Bool :=+  match f as mathValue with+    | _ / #1 ->+        all+          (\n -> n >= 0)+          (matchAll f as mathValue with+            | poly (term _ ((_, $n) :: _) :: _) -> n)+    | _ -> False++-- Normal form of f modulo the polynomials gs.  When gs is a Groebner+-- basis, the result is the unique standard representative of f in+-- the quotient ring; in particular `polyNF gb f = 0` decides ideal+-- membership.  `polyNFWith` additionally takes the atoms to keep+-- (see extendAtoms); `polyNF` uses the name-order default.+-- The reduction itself runs in the free theory; the result is+-- normalized once here, at the boundary back to the rule-aware+-- world.+def polyNF (gs: [MathValue]) (f: MathValue) : MathValue :=+  polyNFWith [] gs f++def polyNFWith (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : MathValue :=+  snd (polyNFStatusWith atoms gs f)++-- Diagnosed variants.  polyNF returns its input unchanged in two+-- silent situations that are painful to tell apart from a genuine+-- nonzero normal form; these return a status string alongside:+--   "ok"        -- an irreducible normal form was reached+--   "fail-open" -- the input was outside the polynomial fragment+--                  (a proper fraction or Laurent exponents);+--                  returned unchanged+--   "fuel"      -- the step budget ran out; the result is reduced+--                  but possibly not a normal form+def polyNFStatus (gs: [MathValue]) (f: MathValue) : (String, MathValue) :=+  polyNFStatusWith [] gs f++def polyNFStatusWith (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : (String, MathValue) :=+  if all isGbPoly (f :: gs)+    then+      let (st, r) := polyNFLoop+                       10000+                       (extendAtoms atoms (polyAtoms (f :: gs)))+                       (filter (\g -> not (g = 0)) gs)+                       f+       in (if st = 0 then "ok" else "fuel", mathNormalize r)+    else ("fail-open", f)++-- The safe one-call forms.  polyNF reduces by the polynomials it is+-- GIVEN; on raw (uncompleted) generators the result is not confluent.+-- idealNF completes them first.  For repeated reduction modulo the+-- same ideal, bind `groebnerBasis gens` once instead.+def idealNF (gens: [MathValue]) (f: MathValue) : MathValue :=+  polyNF (groebnerBasis gens) f++def idealNFWith (atoms: [MathValue]) (gens: [MathValue]) (f: MathValue) : MathValue :=+  polyNFWith atoms (groebnerBasisWith atoms gens) f++-- Equality modulo an ideal, as ONE zero test of the difference.+-- Normalizing the two sides separately can pick different default+-- priority lists (their atom sets differ), giving equal values+-- different normal forms.  Inputs must be polynomials: on fractions+-- or Laurent values the reduction fails open and the test returns+-- False even for equal values -- clear denominators first.+def idealEquals (gens: [MathValue]) (a: MathValue) (b: MathValue) : Bool :=+  idealNF gens (a - b) = 0++--+-- Buchberger's algorithm+--++-- Free-theory arithmetic, for the same reason as in polyNFStep.+def sPolynomial (atoms: [MathValue]) (f: MathValue) (g: MathValue) : MathValue :=+  let ltf := leadingTerm atoms f+      ltg := leadingTerm atoms g+      m := monoLcm ltf ltg+   in ((m /' ltf) *' f) -' ((m /' ltg) *' g)++def basisPairs (gs: [MathValue]) : [(MathValue, MathValue)] :=+  matchAll gs as list mathValue with+    | _ ++ $f :: _ ++ $g :: _ -> (f, g)++def buchbergerLoop (atoms: [MathValue]) (gs: [MathValue]) (pairs: [(MathValue, MathValue)]) : [MathValue] :=+  match pairs as list (mathValue, mathValue) with+    | [] -> gs+    | ($f, $g) :: $rest ->+        let r := polyNFEngine atoms gs (sPolynomial atoms f g)+         in if r = 0+              then buchbergerLoop atoms gs rest+              else buchbergerLoop atoms (gs ++ [r]) (rest ++ map (\h -> (h, r)) gs)++--+-- Reduced Groebner basis+--++def insertByLeadingTerm (atoms: [MathValue]) (g: MathValue) (sorted: [MathValue]) : [MathValue] :=+  match sorted as list mathValue with+    | [] -> [g]+    | $h :: $hs ->+        if grevlexGreater (expVec atoms (leadingTerm atoms g)) (expVec atoms (leadingTerm atoms h))+          then h :: insertByLeadingTerm atoms g hs+          else g :: h :: hs++def sortByLeadingTerm (atoms: [MathValue]) (gs: [MathValue]) : [MathValue] :=+  foldl (\acc g -> insertByLeadingTerm atoms g acc) [] gs++-- Keep only elements whose leading term is not divisible by another+-- kept element's leading term.  gs must be sorted ascending, so a+-- potential divisor always comes first.+def minimizeBasis (atoms: [MathValue]) (gs: [MathValue]) : [MathValue] :=+  foldl+    (\kept g ->+      if any (\h -> monoDivides (leadingTerm atoms h) (leadingTerm atoms g)) kept+        then kept+        else kept ++ [g])+    []+    gs++def makeMonic (atoms: [MathValue]) (g: MathValue) : MathValue :=+  match leadingTerm atoms g as mathValue with+    | term $c _ -> g /' c++def reduceBasis (atoms: [MathValue]) (gs: [MathValue]) : [MathValue] :=+  let minimal := minimizeBasis atoms (sortByLeadingTerm atoms (filter (\g -> not (g = 0)) gs))+      reduced :=+        map+          (\g -> makeMonic atoms (polyNFEngine atoms (deleteFirst g minimal) g))+          minimal+   in sortByLeadingTerm atoms (filter (\g -> not (g = 0)) reduced)++-- The reduced Groebner basis of the ideal generated by gens+-- (monic, mutually reduced, sorted by leading term).  Each element+-- `LT + rest` can be read as the rewrite rule `LT -> -rest`; the+-- rule set is terminating and confluent by construction.+-- `groebnerBasisWith` additionally takes the atoms to keep (see+-- extendAtoms); `groebnerBasis` uses the name-order default.+-- The elements are returned in free form (not re-normalized): like a+-- rule right-hand side, a basis element for atoms that carry auto+-- rules (e.g. a Pythagorean generator) would collapse under its own+-- rules if normalized.+def groebnerBasis (gens: [MathValue]) : [MathValue] :=+  groebnerBasisWith [] gens++def groebnerBasisWith (atoms: [MathValue]) (gens: [MathValue]) : [MathValue] :=+  let gs := filter (\g -> not (g = 0)) gens+      atoms' := extendAtoms atoms (polyAtoms gs)+   in if all isGbPoly gs+        then reduceBasis atoms' (buchbergerLoop atoms' gs (basisPairs gs))+        else gens++--+-- Rule generation for `declare ideal`+--++-- Rewrite pairs (leading term, leading term - g) for each element g of+-- the reduced Groebner basis.  This is the value-level half of+-- `declare ideal`: the declaration desugars to one auto rule that folds+-- these pairs over the value with applyTermRule.  Everything here runs+-- in the free theory (see polyNFStep), so forcing the pair list while+-- the very rule being generated is already registered cannot re-enter+-- the rule engine.  Fail-open: non-polynomial or Laurent generators+-- yield no rules.+def idealTermRules (atoms: [MathValue]) (gens: [MathValue]) : [(MathValue, MathValue)] :=+  let gs := filter (\g -> not (g = 0)) gens+   in if all isGbPoly gs+        then+          let atoms' := extendAtoms atoms (polyAtoms gs)+           in map+                (\g -> let lt := leadingTerm atoms' g in (lt, lt -' g))+                (groebnerBasisWith atoms gs)+        else []++-- Apply the generated rewrite pairs once each (term-level, with the+-- same monomial-containment matching as hand-written+-- `declare rule auto term` rules).  iterateRulesCAS drives this to a+-- fixpoint, and termination and confluence hold because the pairs come+-- from a Groebner basis.+def applyIdealRules (rules: [(MathValue, MathValue)]) (v: MathValue) : MathValue :=+  foldl (\w (l, r) -> applyTermRule l r w) v rules++-- The Pythagorean generators for one angle, ready for polyNF or for+-- concatenation across angles (trigIdeal θ ++ trigIdeal φ).  Built+-- inside the rule-suppression quote: written plainly, the generator+-- would be collapsed to 0 by the built-in Pythagorean auto rules.+-- For program-wide automatic reduction, `declare ideal [(sin θ)^2 ++-- (cos θ)^2 - 1]` registers the same relation as rewrite rules.+def trigIdeal (t: MathValue) : [MathValue] :=+  ['((sin t)^2 + (cos t)^2 - 1)]++--+-- Coefficient-field parameterization (design/cas-simplification.md 3.8):+-- the same engine over a coefficient field given as a pair of+-- operations (reduce, divide).  reduce normalizes a coefficient into+-- the field's canonical representative (e.g. mod p); divide is the+-- field division (over F_p, multiplication by the modular inverse --+-- rational division is meaningless there).  The default engine above+-- is the (id, /') instance and is untouched.+--+-- Restriction: the operations must form a FIELD (prime p); over rings+-- with zero divisors Buchberger's divisions break down.+--++-- Apply the coefficient reduce to every term of a polynomial value.+def reduceCoeffs (red: MathValue -> MathValue) (v: MathValue) : MathValue :=+  match v as mathValue with+    | poly $ts ->+        foldl+          (+')+          0+          (map+            (\t -> match t as mathValue with+              | term $c $xs ->+                  red c *' foldl (*') 1 (map (uncurry (^')) xs))+            ts)+    | _ -> v++-- Monomial quotient with field division on the coefficients; the+-- exponent part is field-independent.+def monoQuotF (fdiv: MathValue -> MathValue -> MathValue) (u: MathValue) (lt: MathValue) : MathValue :=+  match (u, lt) as (mathValue, mathValue) with+    | (term $cu $xu, term $cl $xl) ->+        fdiv cu cl+          *' (foldl (*') 1 (map (uncurry (^')) xu)+                /' foldl (*') 1 (map (uncurry (^')) xl))++def polyNFStepF (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : [MathValue] :=+  take 1+    (matchAll (gs, f) as (multiset mathValue, mathValue) with+      | ($g :: _, poly ((?(monoDivides (leadingTerm atoms g)) & $u) :: _)) ->+          reduceCoeffs red (f -' (monoQuotF fdiv u (leadingTerm atoms g) *' g)))++def polyNFLoopF (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (fuel: Integer) (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : MathValue :=+  if fuel = 0+    then f+    else match polyNFStepF red fdiv atoms gs f as list mathValue with+      | [] -> f+      | $f' :: _ -> polyNFLoopF red fdiv (fuel - 1) atoms gs f'++def polyNFEngineF (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : MathValue :=+  polyNFLoopF red fdiv 10000 atoms (filter (\g -> not (g = 0)) gs) (reduceCoeffs red f)++def sPolynomialF (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (f: MathValue) (g: MathValue) : MathValue :=+  let ltf := leadingTerm atoms f+      ltg := leadingTerm atoms g+      m := monoLcm ltf ltg+   in reduceCoeffs red ((monoQuotF fdiv m ltf *' f) -' (monoQuotF fdiv m ltg *' g))++def buchbergerLoopF (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (gs: [MathValue]) (pairs: [(MathValue, MathValue)]) : [MathValue] :=+  match pairs as list (mathValue, mathValue) with+    | [] -> gs+    | ($f, $g) :: $rest ->+        let r := polyNFEngineF red fdiv atoms gs (sPolynomialF red fdiv atoms f g)+         in if r = 0+              then buchbergerLoopF red fdiv atoms gs rest+              else buchbergerLoopF red fdiv atoms (gs ++ [r]) (rest ++ map (\h -> (h, r)) gs)++def makeMonicF (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (g: MathValue) : MathValue :=+  match leadingTerm atoms g as mathValue with+    | term $c _ -> reduceCoeffs red (fdiv 1 c *' g)++def reduceBasisF (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (gs: [MathValue]) : [MathValue] :=+  let minimal := minimizeBasis atoms (sortByLeadingTerm atoms (filter (\g -> not (g = 0)) gs))+      reduced :=+        map+          (\g -> makeMonicF red fdiv atoms (polyNFEngineF red fdiv atoms (deleteFirst g minimal) g))+          minimal+   in sortByLeadingTerm atoms (filter (\g -> not (g = 0)) reduced)++-- The reduced Groebner basis over the given coefficient field.+def groebnerBasisField (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (gens: [MathValue]) : [MathValue] :=+  let gs := map (reduceCoeffs red) (filter (\g -> not (g = 0)) gens)+      gs' := filter (\g -> not (g = 0)) gs+      atoms' := extendAtoms atoms (polyAtoms gs')+   in if all isGbPoly gs'+        then reduceBasisF red fdiv atoms' (buchbergerLoopF red fdiv atoms' gs' (basisPairs gs'))+        else gens++-- Normal form over the given coefficient field.+def polyNFField (red: MathValue -> MathValue) (fdiv: MathValue -> MathValue -> MathValue) (atoms: [MathValue]) (gs: [MathValue]) (f: MathValue) : MathValue :=+  if all isGbPoly (f :: gs)+    then polyNFEngineF red fdiv (extendAtoms atoms (polyAtoms (f :: gs))) gs f+    else f++-- Finite-field reduce for `declare cas-quotient`: coefficients modulo+-- a prime p, and normal form modulo the ideal of the generators (the+-- minimal polynomial of the primitive element).  This composes the two+-- quotient mechanisms into GF(p^k):+--+--   declare cas-quotient GF4 := MathValue by finiteFieldReduce 2 [α^2 + α + 1]+--+-- The base being MathValue, the resulting type contains not just the+-- field scalars but polynomials over the field: the reduce applies the+-- coefficient discipline inside every term.  The Groebner basis is+-- computed once, when the reduce is built at declaration time.+-- p must be PRIME: the field divisions use Fermat inverses (b^(p-2)),+-- which is no inverse at all over composite moduli.+def finiteFieldReduce (p: Integer) (gens: [MathValue]) : MathValue -> MathValue :=+  let red := \c -> i.modulo c p+      fdiv := \a b -> i.modulo (a * (i.modulo b p)^(p - 2)) p+      gb := groebnerBasisField red fdiv [] gens+   in \v -> polyNFField red fdiv [] gb v
lib/math/algebra/inverse.egi view
@@ -2,8 +2,8 @@ -- Inverse -- -def inverse (t: MathExpr) (f: MathExpr) (x: MathExpr) : MathExpr :=-  match f as mathExpr with+def inverse (t: MathValue) (f: MathValue) (x: MathValue) : MathValue :=+  match f as mathValue with     | ?isSimpleTerm ->       match f as symbolExpr with         | #x -> t@@ -23,12 +23,12 @@            in inverse (t / a) (f / a) x         | _ -> 'inverse t f x     | ?isPolynomial ->-      match coefficients x f as list mathExpr with+      match coefficients x f as list mathValue with         | $c :: (loop $i (1, $n)                    (#0 :: ...)                    ($a :: [])) -> inverse ((t - c) / a) (x ^ (n + 1)) x         | _ -> 'inverse t f x     | _ ->-      match f as mathExpr with+      match f as mathValue with         | $p1 / $p2 -> inverse (p2 * t) p1 x     | _ -> 'inverse t f x
lib/math/algebra/matrix.egi view
@@ -6,15 +6,15 @@   | quadCons (Matrix a) (Matrix a) (Matrix a) (Matrix a)   | matCons Integer Integer a (Matrix a) (Matrix a) (Matrix a) (Matrix a) -def matrix : Matcher (Matrix MathExpr) :=+def matrix : Matcher (Matrix MathValue) :=   matcher-    | quadCons $ $ $ $ as (mathExpr, matrix, matrix, matrix) with+    | quadCons $ $ $ $ as (mathValue, matrix, matrix, matrix) with       | $tgt ->         match tensorShape tgt as list integer with           | $m :: $n :: _ ->             [(tgt_1_1, tgt_1_(2, n), tgt_(2, m)_1, tgt_(2, m)_(2, n))]           | _ -> []-    | matCons #$i #$j $ $ $ $ $ as (mathExpr, matrix, matrix, matrix, matrix) with+    | matCons #$i #$j $ $ $ $ $ as (mathValue, matrix, matrix, matrix, matrix) with       | $tgt ->         let ns := tensorShape tgt             m := nth 1 ns@@ -29,7 +29,53 @@     | $ as (something) with       | $tgt -> [tgt] -def M.inverse (m: Matrix MathExpr) : Matrix MathExpr :=+def M.tensorIndexValue (index: TensorIndex) : MathValue :=+  match index as tensorIndex with+    | (subIndex $x | supIndex $x | diagIndex $x | userIndex $x) -> x++def M.attachOrdinaryIndex {a}+      (value: Matrix a) (index: TensorIndex) : Matrix a :=+  let variance := tensorIndexVariance index+      x := M.tensorIndexValue index+   in match assert "ordinary matrix indices must be up or down"+                   (variance = "down" || variance = "up")+            as bool with+        | #True ->+            if variance = "down"+              then subrefs value [x]+              else suprefs value [x]++-- Preserve an ordinary matrix's explicit index contract after local+-- `withSymbols` indices have become anonymous result axes.+def M.copyOrdinaryIndices {a}+      (source: Matrix a) (value: Matrix a) : Matrix a :=+  let indices := tensorIndices source+   in match assert+              "matrix result must match its source shape and ordinary indices"+              (length (tensorShape source) = 2+               && tensorShape source = tensorShape value+               && (indices = [] || length indices = 2)+               && all+                    (\variance -> variance = "down" || variance = "up")+                    (tensorVariances source))+        as bool with+        | #True -> foldl M.attachOrdinaryIndex value indices++def trace {Ring a} (t: Matrix a) : a :=+  withSymbols [i]+    contractWith (+) t~i_i++def sym {Field a} (a: Matrix a) : Matrix a :=+  M.copyOrdinaryIndices a+    (withSymbols [i, j]+      ((a_i_j + a_j_i) / 2))++def antisym {Field a} (a: Matrix a) : Matrix a :=+  M.copyOrdinaryIndices a+    (withSymbols [i, j]+      ((a_i_j - a_j_i) / 2))++def M.inverse (m: Matrix MathValue) : Matrix MathValue :=   let d := M.det m    in generateTensor         (\[i, j] ->@@ -40,20 +86,26 @@               else - (M.det (M.join A B C D) / d))         (tensorShape m) -def M.* (s: Matrix MathExpr) (t: Matrix MathExpr) : Matrix MathExpr := +def M.* (s: Matrix MathValue) (t: Matrix MathValue) : Matrix MathValue :=    withSymbols [i, j, k] (s~i~j . t_j_k) -def M.*' (s: Matrix MathExpr) (t: Matrix MathExpr) : Matrix MathExpr := +def M.*' (s: Matrix MathValue) (t: Matrix MathValue) : Matrix MathValue :=   withSymbols [i, j, k] (s~i~j .' t_j_k) -def M.power (t: Matrix MathExpr) (k: Integer) : Matrix MathExpr := +-- Matrix-vector multiplication.  M.* indexes its right operand twice+-- (t_j_k), which requires rank 2; a vector operand contracts on the+-- single shared index instead.+def MV.* (s: Matrix MathValue) (t: Vector MathValue) : Vector MathValue :=+  withSymbols [i, j] (s~i~j . t_j)++def M.power (t: Matrix MathValue) (k: Integer) : Matrix MathValue :=    foldl M.* t (take (k - 1) (repeat1 t)) -def M.comm (m1: Matrix MathExpr) (m2: Matrix MathExpr) : Matrix MathExpr := +def M.comm (m1: Matrix MathValue) (m2: Matrix MathValue) : Matrix MathValue :=    withSymbols [i, j, k] m1~i~j . m2_j_k - m2~i~j . m1_j_k -def M.join (A: Matrix MathExpr) (B: Matrix MathExpr) (C: Matrix MathExpr) (D: Matrix MathExpr)-  : Matrix MathExpr :=+def M.join (A: Matrix MathValue) (B: Matrix MathValue) (C: Matrix MathValue) (D: Matrix MathValue)+  : Matrix MathValue :=   let ashape := tensorShape A       bshape := tensorShape B       cshape := tensorShape C@@ -81,7 +133,7 @@ -- -- Determinant ---def M.determinant (m: Matrix MathExpr) : MathExpr :=+def M.determinant (m: Matrix MathValue) : MathValue :=   match tensorShape m as list integer with     | [#0, #0] -> 1     | [$n, #n] ->@@ -90,4 +142,4 @@             sum (map (\o -> product (map2 (\i j -> m_i_j) (between 1 n) o)) os)     | _ -> undefined -def M.det (m: Matrix MathExpr) : MathExpr := M.determinant m+def M.det (m: Matrix MathValue) : MathValue := M.determinant m
lib/math/algebra/root.egi view
@@ -7,54 +7,55 @@ -- -- Root ---def rt (n: MathExpr) (x: MathExpr) : MathExpr :=+declare mathfunc rt+declare apply rt n x :=   if isInteger n     then       if n = 1         then x         else-          match x as mathExpr with+          match x as mathValue with             | #0 -> 0             | ?isMonomial -> rtMonomial n x             | poly $xs / poly $ys ->-                let xd := reduce gcd xs-                    yd := reduce gcd ys+                let xd := reduce gcdForMathValue xs+                    yd := reduce gcdForMathValue ys                     d := rtMonomial n (xd / yd)                  in d *' rt'' n (sum' (map (/' xd) xs) /' sum' (map (/' yd) ys))             | _ -> rt'' n x     else rt'' n x -def rtMonomial (n: MathExpr) (x: MathExpr) : MathExpr :=+def rtMonomial (n: MathValue) (x: MathValue) : MathValue :=   rtTerm n (numerator x * denominator x ^ (n - 1)) / denominator x -def rtTerm (n: MathExpr) (x: MathExpr) : MathExpr :=+def rtTerm (n: MathValue) (x: MathValue) : MathValue :=   match x as termExpr with     | term $a _ ->-      let rtm1 (n: MathExpr) : MathExpr := match n as integer with+      let rtm1 (n: MathValue) : MathValue := match n as integer with                     | #1 -> -1                     | #2 -> i                     | ?isOdd -> -1                     | _ -> undefined        in if a < 0 then rtm1 n *' rtPositiveTerm n (- x) else rtPositiveTerm n x -def rtPositiveTerm (n: MathExpr) (x: MathExpr) : MathExpr :=-  match (n, x) as (mathExpr, mathExpr) with+def rtPositiveTerm (n: MathValue) (x: MathValue) : MathValue :=+  match (n, x) as (mathValue, mathValue) with     | (#3, $a * #i * $r) -> (- i) * rt 3 (a *' r)     | (_, $a * (apply1 #sqrt $b) * $r) -> rt (n * 2) (a ^' 2 *' b) *' rt n r     | (_, $a * (apply2 #rt $n' $b) * $r) -> rt (n * n') (a ^' n' *' b) *' rt n r     | (_, _) -> rtPositiveTerm1 n x   where-    rtPositiveTerm1 (n: MathExpr) (x: MathExpr) : MathExpr :=-      let f (xs: [(MathExpr, MathExpr)]) : (MathExpr, MathExpr) :=-            match xs as assocMultiset mathExpr with+    rtPositiveTerm1 (n: MathValue) (x: MathValue) : MathValue :=+      let f (xs: [(MathValue, MathValue)]) : (MathValue, MathValue) :=+            match xs as assocMultiset mathValue with               | [] -> (1, 1)               | ($p, $k) :: $rs ->                   let (a, b) := f rs                    in (p ^' i.quotient k n *' a, p ^' (k % n) *' b)-          g (n: MathExpr) (x: MathExpr) : MathExpr :=+          g (n: MathValue) (x: MathValue) : MathValue :=             let d := match x as termExpr with                         | term $m $xs ->-                            gcd n (reduce gcd (map snd (toAssoc (pF m) ++ xs)))+                            gcdForMathValue n (reduce gcdForMathValue (map snd (toAssoc (pF m) ++ xs)))              in rt'' (n / d) (rt d x)           in match x as termExpr with             | term $m $xs ->@@ -62,19 +63,64 @@                   | ($a, #1) -> a                   | ($a, $b) -> a *' g n b -def rt'' (n: MathExpr) (x: MathExpr) : MathExpr :=+def rt'' (n: MathValue) (x: MathValue) : MathValue :=   match (n, x) as (integer, integer) with-    | (#2, _) -> 'sqrt x+    | (#2, _) ->+        -- Principal-branch normalization for constant radicands+        -- (design/cas-simplification.md 3.7, option A): a symbol-free+        -- radicand certified negative by interval arithmetic becomes+        -- i * sqrt(-x).  Every surviving sqrt atom then has a positive+        -- radicand, which makes the pair merge sqrt a * sqrt b =+        -- sqrt (a b) sound on principal branches (the formal merge is+        -- off by a sign when both radicands are negative).+        if signOfConst x = "neg"+          then i *' sqrt (- x)+          else sqrtDenest x     | (_, _) -> 'rt n x -def sqrt (x: MathExpr) : MathExpr :=+-- Depth-2 denesting (design/cas-simplification.md 3.6; the classic+-- Borodin-Fagin-Hopcroft-Tompa condition):+--   sqrt (a + b sqrt c), with integers a > 0, b /= 0, c > 0 and+--   a^2 - b^2 c = d^2 a perfect square, denests to+--   sqrt ((a+d)/2) + sign(b) * sqrt ((a-d)/2).+-- Example: sqrt (9 - 4 sqrt 5) = sqrt 5 - 2.  Anything outside this+-- shape (or with a non-square a^2 - b^2 c, e.g. sqrt (-10 - 2 sqrt 5))+-- stays symbolic.  The perfect-square test factorizes a^2 - b^2 c, so+-- it is guarded by a size cap.+def sqrtDenest (x: MathValue) : MathValue :=+  match x as mathValue with+    | poly [term $a [], term $b [(apply1 #sqrt $c, #1)]] ->+        if isInteger a && isInteger b && isInteger c && a > 0 && c > 0+          then+            let r := a^2 - b^2 * c+             in if r > 0 && r <= 1000000000000+                  then sqrtDenest' x a b r+                  else 'sqrt x+          else 'sqrt x+    | _ -> 'sqrt x++def sqrtDenest' (x: MathValue) (a: MathValue) (b: MathValue) (r: MathValue) : MathValue :=+  let facs := toAssoc (pF r)+   in if all (\(_, k) -> i.modulo k 2 = 0) facs+        then+          let d := foldl (\acc (p, k) -> acc * p ^ (i.quotient k 2)) 1 facs+              s := if b > 0 then 1 else -1+           in sqrt ((a + d) / 2) + s * sqrt ((a - d) / 2)+        else 'sqrt x++-- sqrt is split into `declare mathfunc` (declares the function name and+-- registers default symbolic behaviour) plus `declare apply` (the+-- algorithmic simplification at application time). Pattern rewrites such+-- as (sqrt $x)^2 = x live separately as `declare rule auto` declarations+-- in lib/math/normalize.egi.+declare mathfunc sqrt+declare apply sqrt x :=   let m := numerator x       n := denominator x    in rt 2 (m *' n) /' n -def rtOfUnity : MathExpr -> MathExpr := rtu--def rtu (n: MathExpr) : MathExpr :=+declare mathfunc rtu+declare apply rtu n :=   if isInteger n     then match n as integer with       | #1 -> 1@@ -83,3 +129,5 @@       | #4 -> i       | _ -> 'rtu n     else 'rtu n++def rtOfUnity : MathValue -> MathValue := rtu
lib/math/algebra/tensor.egi view
@@ -4,19 +4,57 @@ -- -- +inductive TensorIndex :=+  | SubIndex MathValue+  | SupIndex MathValue+  | DiagIndex MathValue+  | UserIndex MathValue++inductive pattern TensorIndex :=+  | subIndex MathValue+  | supIndex MathValue+  | diagIndex MathValue+  | userIndex MathValue++def tensorIndex : Matcher TensorIndex :=+  algebraicDataMatcher+    | subIndex mathValue+    | supIndex mathValue+    | diagIndex mathValue+    | userIndex mathValue+ infixl expression 7 . infixl expression 7 .'  def tensorOrder {a} (A: Tensor a) : Integer := length (tensorShape A) +def tensorSignature {a} (A: Tensor a) : ([Integer], [TensorIndex]) :=+  (tensorShape A, tensorIndices A)++def tensorIndexVariance (index: TensorIndex) : String :=+  match index as tensorIndex with+    | subIndex _ -> "down"+    | supIndex _ -> "up"+    | diagIndex _ -> "diag"+    | userIndex _ -> "user"++def tensorVariances {a} (A: Tensor a) : [String] :=+  map tensorIndexVariance (tensorIndices A)+ def unitTensor (ns: [Integer]) : Tensor Integer := generateTensor kroneckerDelta ns -def scalarToTensor {Num a} (x: a) (ns: [Integer]) : Tensor a := x * unitTensor ns+def scalarToTensor {MulSemigroup a} (x: a) (ns: [Integer]) : Tensor a := x * unitTensor ns  def zeroTensor (ns: [Integer]) : Tensor Integer := generateTensor (\_ -> 0) ns -def (.') (t1: Tensor MathExpr) (t2: Tensor MathExpr) : Tensor MathExpr := -  foldl1 (+') (contract (t1 *' t2))+-- Reduce every diagonal component produced by `contract` with an explicit+-- reducer.  This is the common contraction kernel used by (.) and (.').+def contractWith {a} (reducer: Tensor a -> Tensor a -> Tensor a)+                     (t: Tensor a) : Tensor a :=+  foldl1 reducer (contract t) -def (.) {Num a} (t1: Tensor a) (t2: Tensor a) : Tensor a := -  foldl1 (+) (contract (t1 * t2))+def (.') (t1: Tensor MathValue) (t2: Tensor MathValue) : Tensor MathValue := +  contractWith (+') (t1 *' t2)++def (.) {Ring a} (t1: Tensor a) (t2: Tensor a) : Tensor a :=+  contractWith (+) (t1 * t2)
lib/math/algebra/vector.egi view
@@ -2,22 +2,22 @@ -- Vectors -- -def dotProduct {Num a} (v1: Tensor a) (v2: Tensor a) : Tensor a := +def dotProduct {Ring a} (v1: Tensor a) (v2: Tensor a) : Tensor a :=    withSymbols [i] v1~i . v2_i -def V.* {Num a} : Tensor a -> Tensor a -> Tensor a := dotProduct+def V.* {Ring a} : Tensor a -> Tensor a -> Tensor a := dotProduct -def crossProductWithFun {Num a} (fn: a -> a -> a) (a: Vector a) (b: Vector a) : Vector a :=+def crossProductWithFun {Ring a} (fn: a -> a -> a) (a: Vector a) (b: Vector a) : Vector a :=   [|fn a_2 b_3 - fn a_3 b_2, fn a_3 b_1 - fn a_1 b_3, fn a_1 b_2 - fn a_2 b_1|] -def crossProduct {Num a} (a: Vector a) (b: Vector a) : Vector a := +def crossProduct {Ring a} (a: Vector a) (b: Vector a) : Vector a :=    crossProductWithFun (*) a b -def div {Num a} (A: Vector a) (xs: Vector a) : a := trace (!∂/∂ A xs)--def rot {Num a} (A: Vector a) (xs: Vector a) : Vector a := -  crossProductWithFun ∂/∂ A xs--def trace {Num a} (t: Matrix a) : a :=-  withSymbols [i] sum (contract t~i_i)+def div {Ring a} (A: Vector a) (xs: Vector a) : a := trace (!∂/∂ A xs) +-- curl: the standard convention (rot A)_i = eps_ijk d(A_k)/d(x_j), so+-- (rot A)_1 = dA_3/dx_2 - dA_2/dx_3.  This is nabla x A, hence the+-- coordinates xs take the first cross-product slot and the derivative+-- is flipped to differentiate the field component by the coordinate.+def rot {Ring a} (A: Vector a) (xs: Vector a) : Vector a :=+  crossProductWithFun (flip ∂/∂) xs A
lib/math/analysis/derivative.egi view
@@ -4,71 +4,150 @@ -- -- -def ∂/∂ (f : Tensor MathExpr) (x : Tensor MathExpr) : Tensor MathExpr :=-  tensorMap2 (\f x -> ∂/∂' f x) f (flipIndices x)-  -def ∂/∂' (f : MathExpr) (!x : MathExpr) : MathExpr :=-  match f as mathExpr with-    -- symbol+-- Differentiable type class. Each CAS shape's instance owns its own+-- structural decomposition rule. Recursive sub-calls go through+-- `partialDiff` (the typeclass method) so runtime-type dispatch picks the+-- right instance again — and the dispatch is O(1) thanks to+-- IRuntimeDispatch reusing the already-evaluated argument (see+-- `evalExprShallow env (IRuntimeDispatch ...)` in Core.hs).+--+-- We don't write an explicit `instance Differentiable MathValue`: the+-- compiler emits `IRuntimeDispatch` for that case, picking the right+-- concrete instance below from the runtime CAS shape.+class Differentiable a where+  partialDiff (f: a) (x: MathValue) : MathValue++instance Differentiable Factor where+  -- Atomic shapes: a single symbol, apply1-4, quote, or func.+  -- Reachable via static dispatch from Term inst's `$fx` (typed Factor through+  -- the `^` pattern's `as (factor, integer)` signature). Symbols short-circuit+  -- here; apply / quote / func delegate to the chain dispatcher.+  partialDiff f x := match f as factor with     | #x -> 1-    | ?isSymbol -> 0+    | symbol _ _ -> 0+    | _ -> chainPartialDiff f x++instance Differentiable (Term MathValue [..]) where+  -- The `^` constructor in `inductive pattern MathValue` is `(^) Factor Integer`,+  -- so `$fx` is statically Factor and `partialDiff fx x` dispatches at compile+  -- time to the Factor instance. No need for in-body atomic short-circuits.+  partialDiff f x := match f as mathValue with+    | #0 -> 0+    | _ * #1 -> 0+    | #1 * $fx ^ $n -> n * fx ^ (n - 1) * partialDiff fx x+    | $a * $fx ^ $n * $r -> a * partialDiff (fx ^' n) x * r + a * fx ^' n * partialDiff r x+    | _ -> 0++instance Differentiable (Poly MathValue [..]) where+  -- Polynomial: sum of per-term derivatives. The `poly` constructor in+  -- `inductive pattern MathValue` is `poly [Term MathValue [..]]`, so each+  -- `$t` has static type `Term MathValue [..]` and `partialDiff t x` dispatches+  -- to the Term instance at compile time — no runtime dispatch needed.+  partialDiff f x := match f as mathValue with+    | poly $ts -> sum (map (\t -> partialDiff t x) ts)+    | _ -> chainPartialDiff f x++instance Differentiable (Frac MathValue) where+  -- Quotient rule.+  partialDiff f x := match f as mathValue with+    | $p1 / $p2 ->+        let p1' := partialDiff p1 x+            p2' := partialDiff p2 x+         in (p1' * p2 - p2' * p1) / p2 ^ 2+    | _ -> chainPartialDiff f x++-- ---------------------------------------------------------------------------+-- Top-level differentiation operator (∂/∂) and the chain dispatcher.+-- ---------------------------------------------------------------------------++-- partialDiffMV is a top-level def whose static type (MathValue ->+-- MathValue -> MathValue) is concrete, so the `partialDiff` inside gets+-- expanded by the typeclass machinery (IRuntimeDispatch) at this+-- definition site. Inlining it as an anonymous lambda inside `∂/∂` would+-- leave the partialDiff call in a context whose static type is still+-- polymorphic by the time TypeClassExpand sees it.+-- Validate the complete CAS value before differentiation so an application+-- without a registered analytic derivative cannot silently become zero.+def partialDiffMV (f : MathValue) (x : MathValue) : MathValue :=+  partialDiff (requireAnalyticDerivative f x) x++def ∂/∂ (f : Tensor MathValue) (x : Tensor MathValue) : Tensor MathValue :=+  tensorMap2 partialDiffMV f (flipIndices x)++-- chainPartialDiff is the user-facing dispatcher for "non-decomposable"+-- shapes — atomic symbols, built-in apply1 derivatives, function+-- expressions and quotes. It does NOT handle term / poly / frac shapes+-- (those have their own typeclass instances above). Each+-- `declare derivative <name>` redefines this var to add new apply1+-- branches before falling back to `chainPartialDiffBuiltin` (which is+-- never redefined), so user-declared derivatives win over the built-in+-- cases without infinite recursion.+def chainPartialDiff (v : MathValue) (dx : MathValue) : MathValue :=+  chainPartialDiffBuiltin v dx++-- chainPartialDiffBuiltin handles the shapes that don't have their own+-- type-class instance: function expressions and quote nodes. apply1+-- cases are dispatched first by the redefined `chainPartialDiff` via+-- `declare derivative` rules; symbols are filtered upstream by Term+-- inst's `#dx`/`?isSymbol` short-circuits. Everything else returns 0+-- (treated as a constant, which is the correct conservative answer for+-- truly opaque values).+def chainPartialDiffBuiltin (v : MathValue) (dx : MathValue) : MathValue :=+  match v as mathValue with     -- function expression     | func _ $args ->-       sum (map2 (\s r -> (userRefs f [s]) * ∂/∂' r x) (between 1 (length args)) args)-    -- function application-    | (apply1 #exp $g) -> exp g * ∂/∂' g x-    | (apply1 #log $g) -> 1 / g * ∂/∂' g x-    | (apply1 #sqrt $g) -> 1 / (2 * sqrt g) * ∂/∂' g x-    --| (apply2 (^) $g $h) -> f * ∂/∂' (log g * h) x-    | (apply1 #cos $g) -> (- sin g) * ∂/∂' g x-    | (apply1 #sin $g) -> cos g * ∂/∂' g x-    --| (apply1 #arccos $g) -> 1 / sqrt (1 - g ^ 2) * ∂/∂' g x-    -- | apply1 $g $a1 ->-    --   `((userRefs g [1]) a1) * ∂/∂' a1 x-    -- | apply2 $g $a1 $a2 ->-    --   `((userRefs g [1]) a1 a2) * ∂/∂' a1 x + `((userRefs g [2]) a1 a2) * ∂/∂' a2 x-    -- | apply3 $g $a1 $a2 $a3 ->-    --   `((userRefs g [1]) a1 a2 a3) * ∂/∂' a1 x + `((userRefs g [2]) a1 a2 a3) * ∂/∂' a2 x + `((userRefs g [3]) a1 a2 a3) * ∂/∂' a3 x-    -- | apply4 $g $a1 $a2 $a3 $a4 ->-    --   `((userRefs g [1]) a1 a2 a3 a4) * ∂/∂' a1 x + `((userRefs g [2]) a1 a2 a3 a4) * ∂/∂' a2 x + `((userRefs g [3]) a1 a2 a3 a4) * ∂/∂' a3 x + `((userRefs g [4]) a1 a2 a3 a4) * ∂/∂' a4 x+       sum (map2 (\s r -> (userRefs v [s]) * partialDiff r dx) (between 1 (length args)) args)+    -- general power u^w with a symbolic exponent: an integer exponent is+    -- a monomial and is decomposed by the Term instance, and e^w never+    -- arrives either ((^) rewrites it to exp w), so what reaches here is+    -- the opaque apply2 form.  (u^w)' = u^w * (w' * log u + w * u' / u);+    -- previously this fell to the final 0 (silently treated as constant).+    | apply2 #(^) $u $w ->+        let u' := partialDiff u dx+            w' := partialDiff w dx+         in v * (w' * log u + w * u' / u)     -- quote     | quote $g ->-      let g' := ∂/∂' g x+      let g' := partialDiff g dx        in if isMonomial g'             then g'-            else let d := foldl1 (\a b -> (gcd a b)) (fromPoly g')+            else let d := foldl1 (\a b -> (gcdForMathValue a b)) (fromPoly g')                   in d *' (mapPoly (/' d) g')-    -- term (constant)-    | #0 -> 0-    | _ * #1 -> 0-    -- term (multiplication)-    | #1 * $fx ^ $n -> n * fx ^ (n - 1) * ∂/∂' fx x-    | $a * $fx ^ $n * $r -> a * ∂/∂' (fx ^' n) x * r + a * fx ^' n * ∂/∂' r x-    -- polynomial-    | poly $ts -> sum (map 1#(∂/∂' $1 x) ts)-    -- quotient-    | $p1 / $p2 ->-      let p1' := ∂/∂' p1 x-          p2' := ∂/∂' p2 x-       in (p1' * p2 - p2' * p1) / p2 ^ 2+    | _ -> 0 -def d/d : MathExpr -> MathExpr -> MathExpr := ∂/∂+-- ---------------------------------------------------------------------------+-- Built-in derivatives. Each `declare derivative <name> = <expr>` rebuilds+-- `chainPartialDiff` to dispatch `apply1 #<name>` through the chain rule+-- using `<expr>` as f'(g) for `f(g(x))`. Adding new derivatives here is+-- equivalent to adding new `apply1 #<name>` cases to a hand-written matcher,+-- but cleaner and uniform with user-declared derivatives.+declare derivative sin = cos+declare derivative cos = \z -> - sin z+declare derivative exp = exp+declare derivative log = \z -> 1 / z+declare derivative sqrt = \z -> 1 / (2 * sqrt z) -def pd/pd : MathExpr -> MathExpr -> MathExpr := ∂/∂+-- ---------------------------------------------------------------------------+-- Aliases.+-- --------------------------------------------------------------------------- -def ∇ : Tensor MathExpr -> Vector MathExpr -> Tensor MathExpr := ∂/∂+def d/d : MathValue -> MathValue -> MathValue := ∂/∂ -def nabla : Tensor MathExpr -> Vector MathExpr -> Tensor MathExpr := ∇+def pd/pd : MathValue -> MathValue -> MathValue := ∂/∂ -def grad : Tensor MathExpr -> Vector MathExpr -> Tensor MathExpr := ∇+def ∇ : Tensor MathValue -> Vector MathValue -> Tensor MathValue := ∂/∂ -def taylorExpansion (f: MathExpr) (x: MathExpr) (a: MathExpr) : [MathExpr] := +def nabla : Tensor MathValue -> Vector MathValue -> Tensor MathValue := ∇++def grad : Tensor MathValue -> Vector MathValue -> Tensor MathValue := ∇++def taylorExpansion (f: MathValue) (x: MathValue) (a: MathValue) : [MathValue] :=   multivariateTaylorExpansion f [|x|] [|a|] -def maclaurinExpansion (f: MathExpr) (x: MathExpr) : [MathExpr] := taylorExpansion f x 0+def maclaurinExpansion (f: MathValue) (x: MathValue) : [MathValue] := taylorExpansion f x 0 -def multivariateTaylorExpansion (f: MathExpr) (xs: Vector MathExpr) (ys: Vector MathExpr) -  : [MathExpr] :=+def multivariateTaylorExpansion (f: MathValue) (xs: Vector MathValue) (ys: Vector MathValue)+  : [MathValue] :=   withSymbols [h]     let hs := generateTensor (\[x] -> h_x) (tensorShape xs)      in map2@@ -80,5 +159,5 @@                 1#(V.substitute hs (withSymbols [i] xs_i - ys_i) $1))              (iterate (compose 1#(∇ $1 xs) 1#(V.* hs $1)) f)) -def multivariateMaclaurinExpansion (f: MathExpr) (xs: Vector MathExpr) : [MathExpr] :=+def multivariateMaclaurinExpansion (f: MathValue) (xs: Vector MathValue) : [MathValue] :=   multivariateTaylorExpansion f xs (tensorMap 1#0 xs)
lib/math/analysis/integral.egi view
@@ -4,8 +4,8 @@ -- -- -def Sd (x : MathExpr) (f : MathExpr) : MathExpr :=-  match f as mathExpr with+def Sd (x : MathValue) (f : MathValue) : MathValue :=+  match f as mathValue with     -- symbols     | #x -> 1 / 2 * x ^ 2     | symbol _ _ -> f * x@@ -32,10 +32,10 @@     | plus $ts / $p2 -> sum (map 1#(Sd x ($1 / p2)) ts)     | $p1 / $p2 -> if containSymbol x p2 then 'Sd x f else Sd x p1 / p2 -def multSd (x: MathExpr) (f: MathExpr) (g: MathExpr) : MathExpr :=+def multSd (x: MathValue) (f: MathValue) (g: MathValue) : MathValue :=   let F := Sd x f    in F * g - Sd x (F * d/d g x) -def dSd (x: MathExpr) (a: MathExpr) (b: MathExpr) (f: MathExpr) : MathExpr :=+def dSd (x: MathValue) (a: MathValue) (b: MathValue) (f: MathValue) : MathValue :=   let F := Sd x f    in substitute [(x, b)] F - substitute [(x, a)] F
lib/math/common/arithmetic.egi view
@@ -3,39 +3,46 @@ -- Arithmetic Operation -- ---declare symbol i, w, e, π: MathExpr--def toMathExpr {a} (arg: a) : MathExpr := mathNormalize (toMathExpr' arg)+declare symbol i, w, e, π: MathValue -def (+') : MathExpr -> MathExpr -> MathExpr := i.+-def (-') : MathExpr -> MathExpr -> MathExpr := i.--def (*') : MathExpr -> MathExpr -> MathExpr := i.*-def (/') : MathExpr -> MathExpr -> MathExpr := i./+def (+') : MathValue -> MathValue -> MathValue := i.++def (-') : MathValue -> MathValue -> MathValue := i.-+def (*') : MathValue -> MathValue -> MathValue := i.*+def (/') : MathValue -> MathValue -> MathValue := i./ -def plusForMathExpr (x: MathExpr) (y: MathExpr) : MathExpr :=+def plusForMathValue (x: MathValue) (y: MathValue) : MathValue :=   mathNormalize (x +' y) -def minusForMathExpr (x: MathExpr) (y: MathExpr) : MathExpr :=+def minusForMathValue (x: MathValue) (y: MathValue) : MathValue :=   mathNormalize (x -' y) -def multForMathExpr (x: MathExpr) (y: MathExpr) : MathExpr :=+def multForMathValue (x: MathValue) (y: MathValue) : MathValue :=   mathNormalize (x *' y) -def divForMathExpr (x: MathExpr) (y: MathExpr) : MathExpr :=+def divForMathValue (x: MathValue) (y: MathValue) : MathValue :=   x /' y -def sum {Num a} (xs: [a]) : a := foldl (+) 0 xs-def sum' (xs: [MathExpr]) : MathExpr := foldl (+') 0 xs+def sum {AddMonoid a} (xs: [a]) : a := foldl (+) zero xs+def sum' (xs: [MathValue]) : MathValue := foldl (+') 0 xs -def product {Num a} (xs: [a]) : a := foldl (*) 1 xs-def product' (xs: [MathExpr]) : MathExpr := foldl (*') 1 xs+def product {MulMonoid a} (xs: [a]) : a := foldl (*) one xs+def product' (xs: [MathValue]) : MathValue := foldl (*') 1 xs -def power (x: MathExpr) (n: MathExpr) : MathExpr := mathNormalize (power' x n)-def power' (x: MathExpr) (n: MathExpr) : MathExpr := foldl (*') 1 (take n (repeat1 x))+def power (x: MathValue) (n: MathValue) : MathValue := mathNormalize (power' x n)+-- power' must avoid any operator that dispatches through mathNormalize so it+-- can be safely called from within `mathNormalize` itself (e.g. inside a+-- declare-rule RHS). foldl/take used `n - 1` whose `-` resolves to+-- `minusForMathValue` -> `mathNormalize` and creates a cycle. Use direct+-- recursion with `i.-` (integer subtraction primitive) and `*'` (the+-- un-normalised multiplication).+def power' (x: MathValue) (n: MathValue) : MathValue :=+  if n = 0+    then 1+    else x *' power' x (i.- n 1) -def exp (x: MathExpr) : MathExpr := 'exp x+def exp (x: MathValue) : MathValue := 'exp x -def (^) (x: MathExpr) (n: MathExpr) : MathExpr :=+def (^) (x: MathValue) (n: MathValue) : MathValue :=   if x = e     then exp n     else if isRational n@@ -44,7 +51,7 @@         else 1 / x ^ i.neg n       else '(^) x n -def (^') (x: MathExpr) (n: MathExpr) : MathExpr :=+def (^') (x: MathValue) (n: MathValue) : MathValue :=   if x = e     then exp n     else if isRational n@@ -53,12 +60,16 @@         else 1 /' x ^' i.neg n       else '(^) x n -def gcd (x: MathExpr) (y: MathExpr) : MathExpr :=+def gcdForMathValue (x: MathValue) (y: MathValue) : MathValue :=   match (x, y) as (termExpr, termExpr) with     | (_, #0) -> x     | (#0, _) -> y     | (term $a $xs, term $b $ys) ->-      gcd' (i.abs a) (i.abs b) *' foldl (*') 1 (map (\(s, n) -> s ^' n) (AC.intersect xs ys))+      -- After Term widening (2026-05-06), `$a` and `$b` are statically MathValue.+      -- Only compute integer gcd when both are integers; otherwise fall back to 1.+      (if isInteger a && isInteger b then gcd' (i.abs a) (i.abs b) else 1)+        *' foldl (*') 1 (map (\(s, n) -> s ^' n) (AC.intersect xs ys))+    | _ -> 1  -- fallback when neither is in term form (e.g., level-3/4 raw poly after tower fix)  def gcd' (x: Integer) (y: Integer) : Integer :=   match (x, y) as (integer, integer) with
lib/math/common/constants.egi view
@@ -2,5 +2,5 @@ -- Mathematical constants -- -def MinkowskiMetric {Num a} : Matrix a :=+def MinkowskiMetric {Ring a} : Matrix a :=   [|[|-1, 0, 0, 0|], [|0, 1, 0, 0|], [|0, 0, 1, 0|], [|0, 0, 0, 1|]|]
lib/math/common/functions.egi view
@@ -2,11 +2,17 @@ -- Mathematical Functions -- -def abs (x: MathExpr) : MathExpr := if isRational x then i.abs x else 'abs x+declare mathfunc abs+declare apply abs x := if isRational x then i.abs x else 'abs x -def neg (x: MathExpr) : MathExpr := if isRational x then i.neg x else - x+-- Mathematical functions are declared via `declare mathfunc` (which gives+-- them a default symbolic-factor wrapper) and then their algorithmic+-- simplification is given via `declare apply`. Pattern rewrite rules such+-- as `i^2 = -1` and `(sqrt $x)^2 = x` live separately as `declare rule auto`+-- declarations in lib/math/normalize.egi. -def exp (x: MathExpr) : MathExpr :=+declare mathfunc exp+declare apply exp x :=   if isTerm x     then match x as termExpr with       | #0 -> 1@@ -15,65 +21,72 @@       | _ -> 'exp x     else 'exp x -def log (x: MathExpr) : MathExpr :=-  match x as mathExpr with+declare mathfunc log+declare apply log x :=+  match x as mathValue with     | #1 -> 0     | #e -> 1     | _ -> 'log x -def cos (x: MathExpr) : MathExpr :=-  match x as mathExpr with+declare mathfunc cos+declare apply cos x :=+  match x as mathValue with     | #0 -> 1     | mult $n #π -> (-1) ^ abs n     | (mult _ #π) / #2 -> 0     | _ -> 'cos x -def sin (x: MathExpr) : MathExpr :=-  match x as mathExpr with+declare mathfunc sin+declare apply sin x :=+  match x as mathValue with     | #0 -> 0     | mult _ #π -> 0     | (mult $n #π) / #2 -> (-1) ^ ((abs n - 1) / 2)     | _ -> 'sin x -def tan (x: MathExpr) : MathExpr :=-  match x as mathExpr with+declare mathfunc tan+declare apply tan x :=+  match x as mathValue with     | #0 -> 0     | _ -> 'tan x ---def acos : MathExpr -> MathExpr := f.acos---def asin : MathExpr -> MathExpr := f.asin---def atan : MathExpr -> MathExpr := f.atan+--def acos : MathValue -> MathValue := f.acos+--def asin : MathValue -> MathValue := f.asin+--def atan : MathValue -> MathValue := f.atan -def cosh (x: MathExpr) : MathExpr :=-  match x as mathExpr with+declare mathfunc cosh+declare apply cosh x :=+  match x as mathValue with     | #0 -> 1     | _ -> 'cosh x -def sinh (x: MathExpr) : MathExpr :=-  match x as mathExpr with+declare mathfunc sinh+declare apply sinh x :=+  match x as mathValue with     | #0 -> 0     | _ -> 'sinh x -def tanh (x: MathExpr) : MathExpr :=-  match x as mathExpr with+declare mathfunc tanh+declare apply tanh x :=+  match x as mathValue with     | #0 -> 0     | _ -> 'tanh x ---def acosh : MathExpr -> MathExpr := f.acosh---def asinh : MathExpr -> MathExpr := f.asinh---def atanh : MathExpr -> MathExpr := f.atanh+--def acosh : MathValue -> MathValue := f.acosh+--def asinh : MathValue -> MathValue := f.asinh+--def atanh : MathValue -> MathValue := f.atanh -def sinc (x: MathExpr) : MathExpr :=-  match x as mathExpr with+def sinc (x: MathValue) : MathValue :=+  match x as mathValue with     | #0 -> 1     | _ -> sin x / x -def sigmoid (z: MathExpr) : MathExpr := 1 / (1 + exp (- z))+def sigmoid (z: MathValue) : MathValue := 1 / (1 + exp (- z))  def kroneckerDelta (js: [Integer]) : Integer :=    if all (= head js) (tail js) then 1 else 0 -def eulerTotientFunction (n: Integer) : MathExpr := +def eulerTotientFunction (n: Integer) : MathValue :=    n * product (map (\p -> 1 - 1 / p) (unique (pF n)))  def ε : Integer -> Tensor Integer :=
+ lib/math/common/interval.egi view
@@ -0,0 +1,141 @@+--+--+-- Rational interval arithmetic for constant expressions+--+--+-- Enclosures with exact rational endpoints, used as SIGN CERTIFICATES+-- for symbol-free values built from rationals and nested square roots+-- (design/cas-simplification.md, Section 3.7).  A numeric point+-- estimate can only suggest a sign; an enclosure that excludes zero+-- proves it.  The consumer is the sqrt application path (root.egi):+-- a constant radicand certified negative is normalized to+-- i * sqrt(-x), which makes every surviving sqrt atom have a+-- positive radicand -- and then the pair merge sqrt a * sqrt b =+-- sqrt (a b) is sound on principal branches.+--++-- Integer square root (floor), by Newton's method.+def iSqrtFloor (n: Integer) : Integer :=+  if n < 2+    then n+    else+      let go (x: Integer) : Integer :=+            let x' := i.quotient (x + i.quotient n x) 2+             in if x' >= x then x else go x'+       in go n++-- Floor of a rational value (MathValue holding p/q).+def ratFloor (v: MathValue) : Integer :=+  let p := numerator v+      q := denominator v+   in if p >= 0+        then i.quotient p q+        else - (i.quotient (- p) q) - (if i.modulo (- p) q = 0 then 0 else 1)++-- Enclosure of the square root of a positive rational interval, at+-- scale 2^k: sqrt(v) is enclosed by [s/2^k, (s'+1)/2^k] with+-- s = isqrt(floor(v_lo * 4^k)) and s' = isqrt(floor(v_hi * 4^k)).+def sqrtInterval (k: Integer) (lo: MathValue) (hi: MathValue) : (MathValue, MathValue) :=+  let scale := 2^k+      s  := iSqrtFloor (ratFloor (lo * scale * scale))+      s' := iSqrtFloor (ratFloor (hi * scale * scale))+   in (s / scale, (s' + 1) / scale)++-- Rational comparisons via the (always positive) denominator:+-- runtime Ord dispatch does not cover fractions, so compare through+-- integer numerators.+def ratLt (a: MathValue) (b: MathValue) : Bool := numerator (a - b) < 0++def ratPos (v: MathValue) : Bool := numerator v > 0++def ratNeg (v: MathValue) : Bool := numerator v < 0++-- Interval product ([min, max] of the endpoint products).+def mulInterval (a: (MathValue, MathValue)) (b: (MathValue, MathValue)) : (MathValue, MathValue) :=+  let (al, ah) := a+      (bl, bh) := b+      ps := [al * bl, al * bh, ah * bl, ah * bh]+   in (minimumMV ps, maximumMV ps)++def minimumMV (xs: [MathValue]) : MathValue :=+  match xs as list mathValue with+    | $x :: $rest -> foldl (\a b -> if ratLt b a then b else a) x rest++def maximumMV (xs: [MathValue]) : MathValue :=+  match xs as list mathValue with+    | $x :: $rest -> foldl (\a b -> if ratLt a b then b else a) x rest++def powInterval (n: Integer) (a: (MathValue, MathValue)) : (MathValue, MathValue) :=+  if n = 1 then a else mulInterval a (powInterval (n - 1) a)++-- Enclosure of a symbol-free value at precision k, or Nothing when+-- the value is outside the supported fragment (free symbols, i,+-- quotes, non-sqrt applications, negative exponents) or a nested+-- radicand cannot be certified positive at this precision.+def constInterval (k: Integer) (v: MathValue) : Maybe (MathValue, MathValue) :=+  match v as mathValue with+    | poly $ts ->+        foldl+          (\acc t -> match (acc, constTermInterval k t) as (maybe something, maybe something) with+            | (just $a, just $b) ->+                let (al, ah) := a+                    (bl, bh) := b+                 in Just (al + bl, ah + bh)+            | (_, _) -> Nothing)+          (Just (0, 0))+          ts+    | _ -> Nothing++def constTermInterval (k: Integer) (t: MathValue) : Maybe (MathValue, MathValue) :=+  match t as mathValue with+    | term $c $xs ->+        if isRational c+          then+            foldl+              (\acc (x, n) ->+                match acc as maybe something with+                  | nothing -> Nothing+                  | just $a ->+                      if n >= 1+                        then match constAtomInterval k x as maybe something with+                          | just $b -> Just (mulInterval a (powInterval n b))+                          | nothing -> Nothing+                        else Nothing)+              (Just (c, c))+              xs+          else Nothing++def constAtomInterval (k: Integer) (x: MathValue) : Maybe (MathValue, MathValue) :=+  match x as mathValue with+    | apply1 #sqrt $a ->+        match constInterval k a as maybe something with+          | just $iv ->+              let (lo, hi) := iv+               in if ratPos lo+                    then Just (sqrtInterval k lo hi)+                    else Nothing+          | nothing -> Nothing+    | _ -> Nothing++-- Certified sign of a symbol-free real value: "pos", "neg", or+-- "unknown" (outside the fragment, or zero cannot be separated even+-- at the highest precision).  Precision escalates until the+-- enclosure excludes zero.+def signOfConst (v: MathValue) : String := signOfConstLoop v [16, 64, 256]++def signOfConstAt (k: Integer) (v: MathValue) : String :=+  match constInterval k v as maybe something with+    | just $iv ->+        let (lo, hi) := iv+         in if ratPos lo then "pos" else if ratNeg hi then "neg" else "escalate"+    | nothing -> "unknown"++def signOfConstLoop (v: MathValue) (ks: [Integer]) : String :=+  match ks as list integer with+    | [] -> "unknown"+    | $k :: $rest ->+        match signOfConstAt k v as string with+          | #"pos" -> "pos"+          | #"neg" -> "neg"+          | #"unknown" -> "unknown"+          | _ -> signOfConstLoop v rest
lib/math/expression.egi view
@@ -4,38 +4,38 @@ -- -- -inductive pattern MathExpr :=-  | div MathExpr MathExpr-  | (/) MathExpr MathExpr-  | plus [MathExpr]-  | poly [MathExpr]-  | term Integer [(MathExpr, Integer)]-  | mult Integer MathExpr-  | (+) MathExpr MathExpr-  | (*) MathExpr MathExpr-  | (^) MathExpr Integer+inductive pattern MathValue :=+  | frac MathValue MathValue+  | (/) MathValue MathValue+  | plus [(Term MathValue [..])]+  | poly [(Term MathValue [..])]+  | term MathValue [(MathValue, Integer)]+  | mult MathValue MathValue+  | (+) (Term MathValue [..]) MathValue+  | (*) MathValue MathValue+  | (^) Factor Integer   | symbol String [IndexExpr]-  | apply1 (MathExpr -> MathExpr) MathExpr-  | apply2 (MathExpr -> MathExpr -> MathExpr) MathExpr MathExpr-  | apply3 (MathExpr -> MathExpr -> MathExpr -> MathExpr) MathExpr MathExpr MathExpr-  | apply4 (MathExpr -> MathExpr -> MathExpr -> MathExpr -> MathExpr) MathExpr MathExpr MathExpr MathExpr-  | quote MathExpr-  | func MathExpr [MathExpr]+  | apply1 (MathValue -> MathValue) MathValue+  | apply2 (MathValue -> MathValue -> MathValue) MathValue MathValue+  | apply3 (MathValue -> MathValue -> MathValue -> MathValue) MathValue MathValue MathValue+  | apply4 (MathValue -> MathValue -> MathValue -> MathValue -> MathValue) MathValue MathValue MathValue MathValue+  | quote MathValue+  | func MathValue [MathValue]  inductive pattern IndexExpr :=-  | sub MathExpr-  | sup MathExpr-  | user MathExpr+  | sub MathValue+  | sup MathValue+  | user MathValue  def indexExpr : Matcher IndexExpr :=   matcher-    | sub $ as (mathExpr) with+    | sub $ as (mathValue) with         | Sub $e -> [e]         | _ -> []-    | sup $ as (mathExpr) with+    | sup $ as (mathValue) with         | Sup $e -> [e]         | _ -> []-    | user $ as (mathExpr) with+    | user $ as (mathValue) with         | User $e -> [e]         | _ -> []     | #$val as () with@@ -43,63 +43,81 @@     | $ as something with         | $tgt -> [tgt] -def mathExpr : Matcher MathExpr :=+def mathValue : Matcher MathValue :=   matcher-    | div $ $ as (mathExpr, mathExpr) with-        | Div $p1 $p2 -> [(p1, p2)]+    | frac $ $ as (mathValue, mathValue) with+        | Frac $p1 $p2 -> [(p1, p2)]         | _ -> []-    | $ / $ as (mathExpr, mathExpr) with-        | Div $p1 $p2 -> [(p1, p2)]+    | $ / $ as (mathValue, mathValue) with+        | Frac $p1 $p2 -> [(p1, p2)]         | _ -> []-    | poly $ as (multiset mathExpr) with-        | Div (Plus $ts) (Plus [Term 1 []]) -> [ts]+    | poly $ as (multiset (term mathValue)) with+        -- Each element is statically Term MathValue [..], so `partialDiff $t x`+        -- in the body dispatches to the Term instance at compile time.+        | Frac (Plus $ts) (Plus [Term 1 []]) -> [ts]         | _ -> []-    | plus $ as (multiset mathExpr) with-        | Div (Plus $ts) (Plus [Term 1 []]) -> [ts]+    | plus $ as (multiset (term mathValue)) with+        | Frac (Plus $ts) (Plus [Term 1 []]) -> [ts]         | _ -> []-    | $ + $ as (mathExpr, mathExpr) with-        | Div (Plus $ts) (Plus [Term 1 []]) ->+    | $ + $ as (term mathValue, mathValue) with+        -- LHS is a single Term (statically), RHS is the rest of the polynomial.+        | Frac (Plus $ts) (Plus [Term 1 []]) ->             matchAll ts as multiset something with               | $t :: $tss -> (t, sum' tss)         | _ -> []-    | term $ $ as (integer, assocMultiset mathExpr) with-        | Div (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+    | term $ $ as (mathValue, assocMultiset mathValue) with+        -- Coefficient is `mathValue` (not `integer`) so that level-4 forms+        -- with Frac coefficients (`CASTerm (CASFrac _ _) _`) are typed+        -- correctly. Runtime PDP `Term $n $xs` extracts whatever coefficient+        -- the CASTerm holds (Integer or Frac).+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->             [(n, xs)]         | _ -> []-    | mult $ $ as (integer, multExpr) with-        | Div (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+    | $ ^ $ as (factor, integer) with+        -- Single-factor monomial decomposition `x ^ n`. Matches a single-term+        -- polynomial with one factor `(x, n)`. The base `$x` is bound with the+        -- `factor` matcher, so its static type is Factor — recursive calls+        -- like `partialDiff $x` then dispatch statically to the Factor instance.+        | $tgt ->+            match tgt as mathValue with+              | term _ (($x, $n) :: []) -> [(x, n)]+              | _ -> []+    | mult $ $ as (mathValue, multExpr) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->             [(n, product' (map (\(x, n) -> x ^' n) xs))]         | _ -> []-    | $ * $ as (integer, multExpr) with-        | Div (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+    | $ * $ as (mathValue, multExpr) with+        -- Leading coefficient widened from `integer` to `mathValue` to allow+        -- Frac coefficients in level-4 polynomial decomposition.+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->             [(n, product' (map (\(x, n) -> x ^' n) xs))]         | _ -> []-    | symbol $ $ as (something, list indexExpr) with-        | Div (Plus [Term 1 [(Symbol $v $js, 1)]]) (Plus [Term 1 []]) ->+    | symbol $ $ as (string, list indexExpr) with+        | Frac (Plus [Term 1 [(Symbol $v $js, 1)]]) (Plus [Term 1 []]) ->             [(v, js)]         | _ -> []-    | apply1 $ $ as (something, mathExpr) with-        | Div (Plus [Term 1 [(Apply1 $v $a1, 1)]]) (Plus [Term 1 []]) ->+    | apply1 $ $ as (something, mathValue) with+        | Frac (Plus [Term 1 [(Apply1 $v $a1, 1)]]) (Plus [Term 1 []]) ->             [(v, a1)]         | _ -> []-    | apply2 $ $ $ as (something, mathExpr, mathExpr) with-        | Div (Plus [Term 1 [(Apply2 $v $a1 $a2, 1)]]) (Plus [Term 1 []]) ->+    | apply2 $ $ $ as (something, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply2 $v $a1 $a2, 1)]]) (Plus [Term 1 []]) ->             [(v, a1, a2)]         | _ -> []-    | apply3 $ $ $ $ as (something, mathExpr, mathExpr, mathExpr) with-        | Div (Plus [Term 1 [(Apply3 $v $a1 $a2 $a3, 1)]]) (Plus [Term 1 []]) ->+    | apply3 $ $ $ $ as (something, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply3 $v $a1 $a2 $a3, 1)]]) (Plus [Term 1 []]) ->             [(v, a1, a2, a3)]         | _ -> []-    | apply4 $ $ $ $ $ as (something, mathExpr, mathExpr, mathExpr, mathExpr) with-        | Div (Plus [Term 1 [(Apply4 $v $a1 $a2 $a3 $a4, 1)]]) (Plus [Term 1 []]) ->+    | apply4 $ $ $ $ $ as (something, mathValue, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply4 $v $a1 $a2 $a3 $a4, 1)]]) (Plus [Term 1 []]) ->             [(v, a1, a2, a3, a4)]         | _ -> []-    | quote $ as (mathExpr) with-        | Div (Plus [Term 1 [(Quote $mexpr, 1)]]) (Plus [Term 1 []]) ->+    | quote $ as (mathValue) with+        | Frac (Plus [Term 1 [(Quote $mexpr, 1)]]) (Plus [Term 1 []]) ->             [mexpr]         | _ -> []-    | func $ $ as (mathExpr, list mathExpr) with-        | Div+    | func $ $ as (mathValue, list mathValue) with+        | Frac             (Plus [Term 1 [(Function $name $args, 1)]])             (Plus [Term 1 []]) ->             [(name, args)]@@ -109,57 +127,275 @@     | $ as something with         | $tgt -> [tgt] -def multExpr : Matcher MathExpr :=+def multExpr : Matcher MathValue :=   matcher-    | ($ ^ $) * $ as (mathExpr, integer, multExpr) with+    | ($ ^ $) * $ as (factor, integer, multExpr) with         | $tgt ->-            matchAll tgt as mathExpr with+            matchAll tgt as mathValue with               | term _ (($x, $n) :: $rs) -> (x, n, product' (map (\(x, n) -> x ^' n) rs))-    | $ ^ $ as (mathExpr, integer) with+    | $ ^ $ as (factor, integer) with         | $tgt ->-            match tgt as mathExpr with+            match tgt as mathValue with               | term _ (($x, $n) :: []) -> [(x, n)]               | _ -> []-    | $ * $ as (mathExpr, multExpr) with+    | $ * $ as (mathValue, multExpr) with+        -- First slot is `x ^' n` — for n > 1 this is no longer a Factor, so keep mathValue.         | $tgt ->-            matchAll tgt as mathExpr with+            matchAll tgt as mathValue with               | term _ (($x, $n) :: $rs) -> (x ^' n, product' (map (\(x, n) -> x ^' n) rs))     | #$val as () with         | $tgt -> if val = tgt then [()] else []     | $ as something with         | $tgt -> [tgt] -def termExpr : Matcher MathExpr := mathExpr+def termExpr : Matcher MathValue := mathValue -def isSymbol (mexpr: MathExpr) : Bool :=-  match mexpr as mathExpr with+-- Phase 5 Step 5.0: basic matchers (atoms, factor, symbol).+-- These deliberately delegate to `mathValue` so that they can be used as+-- generic decomposition matchers under the `factor` / `symbol` names while+-- the full parametric matchers below provide coefficient-aware variants.++def symbol : Matcher MathValue := mathValue++-- factor: matcher for atomic CAS shapes (single symbol, apply1-4, quote, func).+-- Returns Matcher Factor so that pattern variables bound through this matcher+-- get the static type Factor — enabling compile-time dispatch to the+-- `Differentiable Factor` instance from inside Term/Poly instances when the+-- pattern signature is `as (factor, integer)` (e.g. `$fx ^ $n`).+def factor : Matcher Factor :=+  matcher+    | symbol $ $ as (string, list indexExpr) with+        | Frac (Plus [Term 1 [(Symbol $v $js, 1)]]) (Plus [Term 1 []]) ->+            [(v, js)]+        | _ -> []+    | apply1 $ $ as (something, mathValue) with+        | Frac (Plus [Term 1 [(Apply1 $v $a1, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1)]+        | _ -> []+    | apply2 $ $ $ as (something, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply2 $v $a1 $a2, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2)]+        | _ -> []+    | apply3 $ $ $ $ as (something, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply3 $v $a1 $a2 $a3, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2, a3)]+        | _ -> []+    | apply4 $ $ $ $ $ as (something, mathValue, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply4 $v $a1 $a2 $a3 $a4, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2, a3, a4)]+        | _ -> []+    | quote $ as (mathValue) with+        | Frac (Plus [Term 1 [(Quote $mexpr, 1)]]) (Plus [Term 1 []]) ->+            [mexpr]+        | _ -> []+    | func $ $ as (mathValue, list mathValue) with+        | Frac+            (Plus [Term 1 [(Function $name $args, 1)]])+            (Plus [Term 1 []]) ->+            [(name, args)]+        | _ -> []+    | #$val as () with+        | $tgt -> if val = tgt then [()] else []+    | $ as something with+        | $tgt -> [tgt]++-- Phase 5 Step 5.1-5.3: parametric matchers `term`, `poly`, `frac`.+-- They share the same runtime behavior as `mathValue` but the coefficient+-- matcher is parametric (`m` rather than the hard-coded `integer`). Pattern+-- variables bound by `term $`, `mult $`, `$ * $` therefore have type `a` (the+-- coefficient type the user picked), enabling typed decomposition such as+-- `match p as poly (frac integer) with | term $c _ -> ...` where `c : Frac Integer`.+--+-- All three matchers share the structural patterns to ease delegation:+-- a single term is also a polynomial, a polynomial is also a fraction (over 1).+-- This keeps `match (3 * x) as poly integer with | term $c _ -> c` working+-- without requiring callers to coerce manually.++def term {a} (m: MatcherSlot a a) : Matcher (Term a [..]) :=+  matcher+    | term $ $ as (m, assocMultiset mathValue) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) -> [(n, xs)]+        | _ -> []+    | mult $ $ as (m, multExpr) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+            [(n, product' (map (\(x, n) -> x ^' n) xs))]+        | _ -> []+    | $ * $ as (m, multExpr) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+            [(n, product' (map (\(x, n) -> x ^' n) xs))]+        | _ -> []+    | symbol $ $ as (string, list indexExpr) with+        | Frac (Plus [Term 1 [(Symbol $v $js, 1)]]) (Plus [Term 1 []]) ->+            [(v, js)]+        | _ -> []+    | apply1 $ $ as (something, mathValue) with+        | Frac (Plus [Term 1 [(Apply1 $v $a1, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1)]+        | _ -> []+    | apply2 $ $ $ as (something, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply2 $v $a1 $a2, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2)]+        | _ -> []+    | apply3 $ $ $ $ as (something, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply3 $v $a1 $a2 $a3, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2, a3)]+        | _ -> []+    | apply4 $ $ $ $ $ as (something, mathValue, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply4 $v $a1 $a2 $a3 $a4, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2, a3, a4)]+        | _ -> []+    | quote $ as (mathValue) with+        | Frac (Plus [Term 1 [(Quote $mexpr, 1)]]) (Plus [Term 1 []]) -> [mexpr]+        | _ -> []+    | func $ $ as (mathValue, list mathValue) with+        | Frac+            (Plus [Term 1 [(Function $name $args, 1)]])+            (Plus [Term 1 []]) ->+            [(name, args)]+        | _ -> []+    | #$val as () with+        | $tgt -> if val = tgt then [()] else []+    | $ as something with+        | $tgt -> [tgt]++def poly {a} (m: MatcherSlot a a) : Matcher MathValue :=+  matcher+    | poly $ as (multiset (term m)) with+        | Frac (Plus $ts) (Plus [Term 1 []]) -> [ts]+        | _ -> []+    | plus $ as (multiset (term m)) with+        | Frac (Plus $ts) (Plus [Term 1 []]) -> [ts]+        | _ -> []+    | $ + $ as (term m, poly m) with+        | Frac (Plus $ts) (Plus [Term 1 []]) ->+            matchAll ts as multiset something with+              | $t :: $tss -> (t, sum' tss)+        | _ -> []+    -- Delegation to term-level patterns: a single-term value is also a polynomial+    | term $ $ as (m, assocMultiset mathValue) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) -> [(n, xs)]+        | _ -> []+    | mult $ $ as (m, multExpr) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+            [(n, product' (map (\(x, n) -> x ^' n) xs))]+        | _ -> []+    | $ * $ as (m, multExpr) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+            [(n, product' (map (\(x, n) -> x ^' n) xs))]+        | _ -> []+    | symbol $ $ as (string, list indexExpr) with+        | Frac (Plus [Term 1 [(Symbol $v $js, 1)]]) (Plus [Term 1 []]) ->+            [(v, js)]+        | _ -> []+    | apply1 $ $ as (something, mathValue) with+        | Frac (Plus [Term 1 [(Apply1 $v $a1, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1)]+        | _ -> []+    | apply2 $ $ $ as (something, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply2 $v $a1 $a2, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2)]+        | _ -> []+    | apply3 $ $ $ $ as (something, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply3 $v $a1 $a2 $a3, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2, a3)]+        | _ -> []+    | apply4 $ $ $ $ $ as (something, mathValue, mathValue, mathValue, mathValue) with+        | Frac (Plus [Term 1 [(Apply4 $v $a1 $a2 $a3 $a4, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1, a2, a3, a4)]+        | _ -> []+    | quote $ as (mathValue) with+        | Frac (Plus [Term 1 [(Quote $mexpr, 1)]]) (Plus [Term 1 []]) -> [mexpr]+        | _ -> []+    | func $ $ as (mathValue, list mathValue) with+        | Frac+            (Plus [Term 1 [(Function $name $args, 1)]])+            (Plus [Term 1 []]) ->+            [(name, args)]+        | _ -> []+    | #$val as () with+        | $tgt -> if val = tgt then [()] else []+    | $ as something with+        | $tgt -> [tgt]++def frac {a} (m: MatcherSlot a a) : Matcher MathValue :=+  matcher+    | frac $ $ as (m, m) with+        | Frac $p1 $p2 -> [(p1, p2)]+        | _ -> []+    | $ / $ as (m, m) with+        | Frac $p1 $p2 -> [(p1, p2)]+        | _ -> []+    -- Delegation to poly-level patterns: a non-fraction value is also Frac _ 1+    | $ + $ as (mathValue, mathValue) with+        | Frac (Plus $ts) (Plus [Term 1 []]) ->+            matchAll ts as multiset something with+              | $t :: $tss -> (t, sum' tss)+        | _ -> []+    | poly $ as (multiset mathValue) with+        | Frac (Plus $ts) (Plus [Term 1 []]) -> [ts]+        | _ -> []+    -- Delegation to term-level patterns+    | term $ $ as (mathValue, assocMultiset mathValue) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) -> [(n, xs)]+        | _ -> []+    | mult $ $ as (mathValue, multExpr) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+            [(n, product' (map (\(x, n) -> x ^' n) xs))]+        | _ -> []+    | $ * $ as (mathValue, multExpr) with+        | Frac (Plus [Term $n $xs]) (Plus [Term 1 []]) ->+            [(n, product' (map (\(x, n) -> x ^' n) xs))]+        | _ -> []+    | symbol $ $ as (string, list indexExpr) with+        | Frac (Plus [Term 1 [(Symbol $v $js, 1)]]) (Plus [Term 1 []]) ->+            [(v, js)]+        | _ -> []+    | apply1 $ $ as (something, mathValue) with+        | Frac (Plus [Term 1 [(Apply1 $v $a1, 1)]]) (Plus [Term 1 []]) ->+            [(v, a1)]+        | _ -> []+    | quote $ as (mathValue) with+        | Frac (Plus [Term 1 [(Quote $mexpr, 1)]]) (Plus [Term 1 []]) -> [mexpr]+        | _ -> []+    | #$val as () with+        | $tgt -> if val = tgt then [()] else []+    | $ as something with+        | $tgt -> [tgt]++-- mathExpr: an alias for `mathValue` used by older code and the parametric+-- matcher tests. Kept for backward compatibility.+def mathExpr : Matcher MathValue := mathValue+++def isSymbol (mexpr: MathValue) : Bool :=+  match mexpr as mathValue with     | symbol _ _ -> True     | _ -> False -def isApply (mexpr: MathExpr) : Bool :=-  match mexpr as mathExpr with+def isApply (mexpr: MathValue) : Bool :=+  match mexpr as mathValue with     | apply1 _ _ -> True     | apply2 _ _ _ -> True     | apply3 _ _ _ _ -> True     | apply4 _ _ _ _ _ -> True     | _ -> False -def isSimpleTerm (mexpr: MathExpr) : Bool := isSymbol mexpr || isApply mexpr+def isSimpleTerm (mexpr: MathValue) : Bool := isSymbol mexpr || isApply mexpr -def isTerm (mexpr: MathExpr) : Bool :=-  match mexpr as mathExpr with+def isTerm (mexpr: MathValue) : Bool :=+  match mexpr as mathValue with     | term _ _ -> True     | #0 -> True     | _ -> False -def isPolynomial (mexpr: MathExpr) : Bool :=-  match mexpr as mathExpr with+def isPolynomial (mexpr: MathValue) : Bool :=+  match mexpr as mathValue with     | poly _ -> True     | #0 -> True     | _ -> False -def isMonomial (mexpr: MathExpr) : Bool :=-  match mexpr as mathExpr with+def isMonomial (mexpr: MathValue) : Bool :=+  match mexpr as mathValue with     | poly [term _ _] / poly [term _ _] -> True     | #0 -> True     | _ -> False@@ -167,41 +403,46 @@ -- -- Accessor ---def fromMonomial (mexpr: MathExpr) : (MathExpr, MathExpr) :=-  match mexpr as mathExpr with+def fromMonomial (mexpr: MathValue) : (MathValue, MathValue) :=+  match mexpr as mathValue with     | term $a $xs / term $b $ys ->       (a / b, foldl (*') 1 (map (uncurry (^')) xs) / foldl (*') 1 (map (uncurry (^')) ys))  -- -- Map ---def mapPolys (fn: MathExpr -> MathExpr) (mexpr: MathExpr) : MathExpr :=-  match mexpr as mathExpr with+def mapPolys (fn: MathValue -> MathValue) (mexpr: MathValue) : MathValue :=+  match mexpr as mathValue with     | $p1 / $p2 -> fn p1 /' fn p2 -def fromPoly (mexpr: MathExpr) : [MathExpr] :=-  match mexpr as mathExpr with+def fromPoly (mexpr: MathValue) : [MathValue] :=+  match mexpr as mathValue with     | poly $ts1 / $q -> map (\t1 -> t1 /' q) ts1 -def mapPoly (fn: MathExpr -> MathExpr) (mexpr: MathExpr) : MathExpr :=-  match mexpr as mathExpr with+def mapPoly (fn: MathValue -> MathValue) (mexpr: MathValue) : MathValue :=+  match mexpr as mathValue with     | poly $ts1 / $q -> foldl (+') 0 (map (\t1 -> fn (t1 /' q)) ts1) -def mapTerms (fn: MathExpr -> MathExpr) (mexpr: MathExpr) : MathExpr :=-  match mexpr as mathExpr with+def mapTerms (fn: MathValue -> MathValue) (mexpr: MathValue) : MathValue :=+  match mexpr as mathValue with     | poly $ts1 / poly $ts2 ->         foldl (+') 0 (map fn ts1) /' foldl (+') 0 (map fn ts2) -def mapSymbols (fn: MathExpr -> MathExpr) (mexpr: MathExpr) : MathExpr :=+def mapSymbols (fn: MathValue -> MathValue) (mexpr: MathValue) : MathValue :=   mapTerms-    (\match as mathExpr with+    (\match as mathValue with       | term $a $xs ->           a *' foldl                 (*')                 1                 (map-                  (\(x, n) -> match x as mathExpr with+                  (\(x, n) -> match x as mathValue with                       | symbol _ _ -> fn x ^' n+                      | quote $q ->+                          let q' := mapSymbols fn q+                          in if q = q'+                              then x ^' n+                              else quoteScalar q' ^' n                       | apply1 $g $a1 ->                           let a1' := mapSymbols fn a1                           in if a1 = a1'@@ -236,101 +477,47 @@                   xs))     mexpr -def scanAllTerms (mexpr: MathExpr) (f: MathExpr -> Bool) : Bool :=-  match mexpr as mathExpr with+def scanAllTerms (mexpr: MathValue) (f: MathValue -> Bool) : Bool :=+  match mexpr as mathValue with     | poly $ts1 / poly $ts2 -> any f (ts1 ++ ts2)     | _ -> not ((debug2 "scanAllTerms" mexpr) = mexpr) -- TODO: if tensorMap is inserted correctly, we canremove this -def containSymbol (x: MathExpr) (mexpr: MathExpr) : Bool :=+def containSymbol (x: MathValue) (mexpr: MathValue) : Bool :=   scanAllTerms mexpr-    (\t -> match t as mathExpr with+    (\t -> match t as mathValue with       | term _ $xs ->           any-            (\(y, _) -> match y as mathExpr with+            (\(y, _) -> match y as mathValue with               | #x -> True               | apply1 _ $a1 -> containSymbol x a1               | apply2 _ $a1 $a2 -> containSymbol x a1 || containSymbol x a2               | apply3 _ $a1 $a2 $a3 -> containSymbol x a1 || containSymbol x a2 || containSymbol x a3               | apply4 _ $a1 $a2 $a3 $a4 -> containSymbol x a1 || containSymbol x a2 || containSymbol x a3 || containSymbol x a4               | _ -> False)-            xs)--def containFunction1 (f : MathExpr -> MathExpr) (mexpr: MathExpr) : Bool :=-  scanAllTerms mexpr-    (\t -> match t as mathExpr with-      | term _ $xs ->-          any-            (\(y, _) -> match y as mathExpr with-              | apply1 #f _ -> True-              | apply1 _ $a1 -> containFunction1 f a1-              | apply2 _ $a1 $a2 -> containFunction1 f a1 || containFunction1 f a2-              | apply3 _ $a1 $a2 $a3 -> containFunction1 f a1 || containFunction1 f a2 || containFunction1 f a3-              | apply4 _ $a1 $a2 $a3 $a4 -> containFunction1 f a1 || containFunction1 f a2 || containFunction1 f a3 || containFunction1 f a4-              | _ -> False) xs)--def containFunction2 (f : MathExpr -> MathExpr -> MathExpr) (mexpr: MathExpr) : Bool :=-  scanAllTerms mexpr-    (\t -> match t as mathExpr with-      | term _ $xs ->-          any-            (\(y, _) -> match y as mathExpr with-              | apply2 #f _ _ -> True-              | apply1 _ $a1 -> containFunction2 f a1-              | apply2 _ $a1 $a2 -> containFunction2 f a1 || containFunction2 f a2-              | apply3 _ $a1 $a2 $a3 -> containFunction2 f a1 || containFunction2 f a2 || containFunction2 f a3-              | apply4 _ $a1 $a2 $a3 $a4 -> containFunction2 f a1 || containFunction2 f a2 || containFunction2 f a3 || containFunction2 f a4-              | _ -> False)-            xs)--def containFunction3 (f : MathExpr -> MathExpr -> MathExpr -> MathExpr) (mexpr: MathExpr) : Bool :=-  scanAllTerms mexpr-    (\t -> match t as mathExpr with-      | term _ $xs ->-          any-            (\(y, _) -> match y as mathExpr with-              | apply3 #f _ _ _ -> True-              | apply1 _ $a1 -> containFunction3 f a1-              | apply2 _ $a1 $a2 -> containFunction3 f a1 || containFunction3 f a2-              | apply3 _ $a1 $a2 $a3 -> containFunction3 f a1 || containFunction3 f a2 || containFunction3 f a3-              | apply4 _ $a1 $a2 $a3 $a4 -> containFunction3 f a1 || containFunction3 f a2 || containFunction3 f a3 || containFunction3 f a4-              | _ -> False)-            xs)--def containFunction4 (f : MathExpr -> MathExpr -> MathExpr -> MathExpr -> MathExpr) (mexpr: MathExpr) : Bool :=-  scanAllTerms mexpr-    (\t -> match t as mathExpr with-      | term _ $xs ->-          any-            (\(y, _) -> match y as mathExpr with-              | apply4 #f _ _ _ _ -> True-              | apply1 _ $a1 -> containFunction4 f a1-              | apply2 _ $a1 $a2 -> containFunction4 f a1 || containFunction4 f a2-              | apply3 _ $a1 $a2 $a3 -> containFunction4 f a1 || containFunction4 f a2 || containFunction4 f a3-              | apply4 _ $a1 $a2 $a3 $a4 -> containFunction4 f a1 || containFunction4 f a2 || containFunction4 f a3 || containFunction4 f a4-              | _ -> False)-            xs)+            xs+      | _ -> False)  -- -- Substitute ---def substitute (ls: [(MathExpr, MathExpr)]) (mexpr: MathExpr) : MathExpr :=-  match ls as list (mathExpr, mathExpr) with+def substitute (ls: [(MathValue, MathValue)]) (mexpr: MathValue) : MathValue :=+  match ls as list (mathValue, mathValue) with     | [] -> mathNormalize mexpr     | ($x, $a) :: $rs -> substitute rs (substitute' x a mexpr) -def substitute' (x: MathExpr) (a: MathExpr) (mexpr: MathExpr) : MathExpr := +def substitute' (x: MathValue) (a: MathValue) (mexpr: MathValue) : MathValue :=    mapSymbols (rewriteSymbol x a) mexpr -def rewriteSymbol (x: MathExpr) (a: MathExpr) (sexpr: MathExpr) : MathExpr :=-  match sexpr as mathExpr with+def rewriteSymbol (x: MathValue) (a: MathValue) (sexpr: MathValue) : MathValue :=+  match sexpr as mathValue with     | #x -> a     | _ -> sexpr -def V.substitute (xs: Vector MathExpr) (ys: Vector MathExpr) (mexpr: MathExpr) : MathExpr :=+def V.substitute (xs: Vector MathValue) (ys: Vector MathValue) (mexpr: MathValue) : MathValue :=   substitute (zip (tensorToList xs) (tensorToList ys)) mexpr -def expandAll (mexpr: MathExpr) : MathExpr :=-  match mexpr as mathExpr with+def expandAll (mexpr: MathValue) : MathValue :=+  match mexpr as mathValue with     | ?isInteger -> mexpr     | ?isSymbol -> mexpr     -- function application@@ -347,8 +534,8 @@     -- quotient     | $p1 / $p2 -> expandAll p1 / expandAll p2 -def expandAll' (mexpr: MathExpr) : MathExpr :=-  match mexpr as mathExpr with+def expandAll' (mexpr: MathValue) : MathValue :=+  match mexpr as mathValue with     | ?isInteger -> mexpr     | ?isSymbol -> mexpr     -- function application@@ -368,28 +555,40 @@ -- -- Coefficient ---def coefficients (f: MathExpr) (x: MathExpr) : [MathExpr] :=-  let m := maximum (0 :: (matchAll f as mathExpr with+def coefficients (f: MathValue) (x: MathValue) : [MathValue] :=+  let m := maximum (0 :: (matchAll f as mathValue with                            | poly (term $a ((#x, $k) :: $ts) :: _) / _ -> k))   in map (coefficient f x) (between 0 m) -def coefficient (f: MathExpr) (x: MathExpr) (m: Integer) : MathExpr :=+-- Use the matcher's `_ / $d` extraction (PDFracPat) for the divisor so+-- it is consistent with the `poly (...) / _` arm above: PDFracPat returns+-- (self, 1) for non-CASFrac forms (level-4 polys with Frac coefficients),+-- so no spurious division happens. The user-facing `denominator` primitive+-- would return the LCM of Frac denoms here and double-count.+def fracDenom (f: MathValue) : MathValue :=+  match f as mathValue with | _ / $d -> d++def coefficient (f: MathValue) (x: MathValue) (m: Integer) : MathValue :=   if m = 0-    then sum (matchAll f as mathExpr with+    then sum (matchAll f as mathValue with                | poly (term $a (!((#x, _) :: _) & $ts) :: _) / _ ->-                 foldl (*') a (map (uncurry (^')) ts)) / denominator f+                 foldl (*') a (map (uncurry (^')) ts)) / fracDenom f     else coefficient' f x m -def coefficient' (f: MathExpr) (x: MathExpr) (m: Integer) : MathExpr :=+def coefficient' (f: MathValue) (x: MathValue) (m: Integer) : MathValue :=   sum-    (matchAll f as mathExpr with+    (matchAll f as mathValue with       | poly (term $a ((#x, #m) :: (!((#x, _) :: _) & $ts)) :: _) /_ ->-        foldl (*') a (map (uncurry (^')) ts)) /' denominator f+        foldl (*') a (map (uncurry (^')) ts)) /' fracDenom f -def L./ (xs: [MathExpr]) (ys: [MathExpr]) : ([MathExpr], [MathExpr]) :=+def L./ (xs: [MathValue]) (ys: [MathValue]) : ([MathValue], [MathValue]) :=   if length xs < length ys     then ([], xs)-    else match (ys, xs) as (list mathExpr, list mathExpr) with+    else match (ys, xs) as (list mathValue, list mathValue) with       | ($y :: $yrs, $x :: $xrs) ->         let (zs, rs) := L./ (map2 (-) (take (length yrs) xrs) (map (* (x / y)) yrs) ++ drop (length yrs) xrs) ys          in (x / y :: zs, rs)++-- Phase A.5 primitives `mapPolyAll`, `mapTermAll`, `mapFracAll` are+-- registered in `Type/Check.hs`, so they're known to the type system+-- without needing forwarder definitions here.
lib/math/geometry/3d-euclidean-space.egi view
@@ -1,6 +1,6 @@-def coordinates {Num a} : Vector a := [x, y, z]+def coordinates {Ring a} : Vector a := [x, y, z] -def metric {Num a} : Matrix a :=+def metric {Ring a} : Matrix a :=   generateTensor     (\match as list integer with       | [$n, #n] -> 1
lib/math/geometry/4d-euclidean-space.egi view
@@ -1,6 +1,6 @@-def coordinates {Num a} : Vector a := [x, y, z, w]+def coordinates {Ring a} : Vector a := [x, y, z, w] -def metric {Num a} : Matrix a :=+def metric {Ring a} : Matrix a :=   generateTensor     (\match as list integer with       | [$n, #n] -> 1
lib/math/geometry/differential-form.egi view
@@ -1,4 +1,4 @@-def dfNormalize {Num a} (X: DiffForm a) : DiffForm a :=+def dfNormalize {Field a} (X: DiffForm a) : DiffForm a :=   let p := dfOrder X       (es, os) := evenAndOddPermutations p    in withSymbols [i]@@ -6,21 +6,21 @@        - sum (map (\σ -> subrefs X (map 1#i_(σ $1) (between 1 p))) os))        / fact p -def antisymmetrize {Num a} : DiffForm a -> DiffForm a := dfNormalize+def antisymmetrize {Field a} : DiffForm a -> DiffForm a := dfNormalize -def wedge {Num a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a := X !. Y+def wedge {Ring a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a := X !. Y  infixl expression 7 ∧ -def (∧) {Num a} : DiffForm a -> DiffForm a -> DiffForm a := wedge+def (∧) {Ring a} : DiffForm a -> DiffForm a -> DiffForm a := wedge -def Lie.wedge {Num a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a := +def Lie.wedge {Ring a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a :=   X !. Y - Y !. X -def ι {Num a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a := +def ι {Field a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a :=    withSymbols [i] dfOrder Y * (X...~i . dfNormalize Y..._i) ---def Lie {Num a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a :=+--def Lie {Field a} (X: DiffForm a) (Y: DiffForm a) : DiffForm a := --  match dfOrder Y as integer with --    | #0 -> ι X (d Y) --    | #N -> d (ι X Y)
lib/math/geometry/minkowski-space.egi view
@@ -1,6 +1,6 @@-def coordinates {Num a} : Vector a := [t, x, y, z]+def coordinates {Ring a} : Vector a := [t, x, y, z] -def metric {Num a} : Matrix a :=+def metric {Ring a} : Matrix a :=   generateTensor     (\match as list integer with       | [#1, #1] -> -1
lib/math/no-normalize.egi view
@@ -4,4 +4,4 @@ -- -- -def mathNormalize : (MathExpr -> MathExpr) := id+def mathNormalize : (MathValue -> MathValue) := id
lib/math/normalize.egi view
@@ -4,39 +4,144 @@ -- -- -def mathNormalize (x: MathExpr) : MathExpr :=-  if isInteger x-    then x-    else match (containFunction1 rtu x, containFunction1 sin x || containFunction1 cos x) as (bool, bool) with-           | (#False, #False) -> symbolNormalize x-           | (#True, #False)  -> rewriteRuleForRtu (symbolNormalize x)-           | (#False, #True)  -> rewriteRuleForSinAndCos (symbolNormalize x)-           | (#True, #True)  -> rewriteRuleForSinAndCos (rewriteRuleForRtu (symbolNormalize x))+-- Built-in normalization. Now reduced to the residual Haskell-side rewriter+-- (`symbolNormalize` -> `casRewriteSymbol` -> `casRewriteDd`); all other+-- rewrite rules live as `declare rule auto` declarations below.+--+-- The user-facing `mathNormalize` is defined as an alias that desugar+-- overrides per `declare rule auto`. The override pattern is:+--+--   def mathNormalize := \v -> iterateRulesCAS [autoRule.0, ...] (mathNormalizeBuiltin v)+--+-- so user-declared auto rules apply on each `mathNormalize` call (which is+-- triggered by every `+`/`*`/`/` operation on MathValue, see arithmetic.egi).+def mathNormalizeBuiltin (x: MathValue) : MathValue := symbolNormalize x +def mathNormalize (x: MathValue) : MathValue := mathNormalizeBuiltin x+ ----- rtu+-- Built-in auto rules (migrated from Math/Rewrite.hs).+-- Each `declare rule auto` extends mathNormalize so the rule fires on every+-- arithmetic operation (+, -, *, /, ^). ---def rewriteRuleForRtu : MathExpr -> MathExpr := mapPolys rewriteRuleForRtuPoly-  where-    rewriteRuleForRtuPoly (x: MathExpr) : MathExpr :=-      match x as mathExpr with-        | $a * (apply1 #rtu $n) ^ #1 * $mr + (loop $i (2, (n - 1))-                                       (#a * (apply1 #rtu #n) ^ #i * #mr + ...)-                                       $pr) ->-          rewriteRuleForRtuPoly (pr +' (-1) *' a *' mr)-        | _ -> x +-- Imaginary unit: i^2 = -1.+-- Replaces casRewriteI in Math/Rewrite.hs. The auto-rule engine handles+-- arbitrary powers (i^3, i^4, ...) by iterating the rule on each term.+declare rule auto term i^2 = -1++-- Cube root of unity: the primitive cube root w satisfies w^2 + w + 1 = 0.+-- Declared as an ideal (G3 of design/cas-simplification.md): the Groebner+-- basis of the generator is computed once and registered as term-level+-- rewrite rules.  The single generated rule w^2 -> -1 - w subsumes the+-- previously hand-written pair (w^3 = w * w^2 reduces in two steps), so+-- the completeness of the rule set is a theorem instead of a convention.+declare ideal [w^2 + w + 1]++-- Logarithm identities (replaces casRewriteLog in Math/Rewrite.hs).+-- log 1 = 0 and log e = 1 are also handled at the lib `log` function entry,+-- but are kept here so the symbolic 'log form normalises consistently.+-- log (e^n) reaches the rule as log (exp n) because (^) rewrites e^n to exp n+-- in lib/math/common/arithmetic.egi.+declare rule auto term log 1 = 0+declare rule auto term log e = 1+declare rule auto term log (exp $n) = n++-- Exponential identities (replaces casRewriteExp in Math/Rewrite.hs).+-- The multi-factor cases ((exp x)^n, exp x * exp y) are now expressible+-- because lib/math/expression.egi was extended with apply1-4 patterns on+-- the multExpr matcher (so the "rest of factors" slot accepts apply1).+declare rule auto term exp 0 = 1+declare rule auto term exp 1 = e+declare rule auto term exp ($n * i * π) = (-1)^n+-- The structural exp rules ((exp x)^n -> exp (n x) and the multi-factor+-- product merge) are implemented in Haskell (Math/Rewrite.hs,+-- casRewriteExp), for the same per-term match-cost reason as the sqrt+-- rules; the value rules above stay here.++-- Power identities (replaces casRewritePower in Math/Rewrite.hs).+-- (x^y)^n = x^(n*y) for n >= 2; n=1 returns the term unchanged.+-- The pattern matches a single-factor term containing one apply2 #(^) factor.+declare rule auto term term $c ((apply2 #(^) $x $y, $n) :: []) =+  if n >= 2+    then c *' '(^) x (n *' y)+    else c *' '(^) x y++-- x^y * x^z = x^(y+z) when both factors are apply2 #(^) with the same base.+-- Exact 2-factor variant; multi-factor cases stay in casRewritePower for now.+declare rule auto term term $c ((apply2 #(^) $x $y, #1) :: (apply2 #(^) #x $z, #1) :: []) =+  c *' '(^) x (y +' z)++-- nth-root power reduction (replaces casRewriteRt in Math/Rewrite.hs).+-- (rt n x)^k for k >= n -> (rt n x)^(k mod n) * x^(k div n).+-- The isInteger guard skips the rule for symbolic n.+declare rule auto term term $c ((apply2 #rt $n $x, $k) :: []) =+  if isInteger n+    then if k >= n+      then c *' x^'(i.quotient k n) *' ('rt n x)^'(i.modulo k n)+      else c *' ('rt n x)^'k+    else c *' ('rt n x)^'k++-- nth-root-of-unity power reduction (replaces casRewriteRtu in+-- Math/Rewrite.hs).+-- - k >= n: reduce exponent via mod n.+-- - k = n-1: apply minimal-polynomial reduction+--   (rtu n)^(n-1) = -1 - rtu n - (rtu n)^2 - ... - (rtu n)^(n-2).+-- - otherwise: keep as-is.+-- Note: the original casRewriteRtu g stage was buggy (foldr casMinus gave+-- alternating signs); the foldl form below is the correct minimal-polynomial.+declare rule auto term term $c ((apply1 #rtu $n, $k) :: []) =+  if isInteger n+    then if k >= n+      then c *' ('rtu n)^'(i.modulo k n)+      else if k = n - 1+        then c *' (foldl (+') (-1) (map (\j -> (-1) *' ('rtu n) ^' j) (between 1 (n - 2))))+        else c *' ('rtu n)^'k+    else c *' ('rtu n)^'k++-- Square root power reduction and pair merging are implemented in+-- Haskell (Math/Rewrite.hs, casRewriteSqrt): the declare-rule versions+-- paid a pattern-match attempt on every term of every sqrt-carrying+-- value per normalization, making arithmetic on such values ~20x+-- slower (thurston.egi's bottleneck; design/cas-simplification.md G6).++-- Note: FunctionData same-shape term merging stays in casRewriteDd+-- (Math/Rewrite.hs). The poly-level declare rule version+--   $a * ($f & func $g $args) * $mr + $b * (func #g #args) * #mr + $rest+--     = (a + b) * f * mr + rest+-- works correctly but the multi-term + same-binding constraint is too+-- expensive for complex differential-form computations like+-- riemann-curvature-tensor-of-S2xS3 (>120s vs few-seconds with Haskell).++-- abs of a manifestly non-negative monomial reduces to the monomial itself.+-- A monomial is non-negative if all its symbol exponents are even AND the+-- coefficient is a non-negative rational. This handles common cases like+-- `abs(sin²θ * r⁴)` (showing up in spherical Laplacians, hodge-spherical+-- etc.) by stripping the abs wrapper. ----- sin and cos+-- The LHS pattern triggers on any abs factor; the body re-matches the inner+-- argument as a single Term and checks the conditions before stripping.+declare rule auto term term $c ((apply1 #abs $a, $n) :: $rr) =+  match a as termExpr with+    | term $aCoeff $aMs ->+        if isRational aCoeff && aCoeff >= 0+            && all (\(_, k) -> i.modulo k 2 = 0) aMs+          then c *' (foldl (*') aCoeff (map (\(p, k) -> p ^' k) aMs)) ^' n+                 *' foldl (*') 1 (map (\(p, k) -> p ^' k) rr)+          else c *' ('abs a) ^' n+                 *' foldl (*') 1 (map (\(p, k) -> p ^' k) rr)+    | _ -> c *' ('abs a) ^' n+             *' foldl (*') 1 (map (\(p, k) -> p ^' k) rr)+ ---def rewriteRuleForSinAndCos : MathExpr -> MathExpr := mapPolys rewriteRuleForSinAndCosPoly-  where-    rewriteRuleForSinAndCosPoly (x: MathExpr) : MathExpr :=-      match x as mathExpr with-        | $a * $mr + #(- a) * (apply1 #cos $x) ^ #2 * #mr + $pr ->-          rewriteRuleForSinAndCosPoly (a *' (sin x)^2 *' mr +' pr)---        | $a * $mr + #(- a) * (apply1 #sin $x) ^ #2 * #mr + $pr ->---          rewriteRuleForSinAndCosPoly (a *' (cos x)^2 *' mr +' pr)-        | $a * (apply1 #cos $x) ^ #2 * $mr + $b * (apply1 #sin #x) ^ #2 * #mr + $pr ->-          rewriteRuleForSinAndCosPoly (a *' mr +' (b -' a) *' (sin x)^2 *' mr +' pr)-        | _ -> x+-- sin/cos pythagorean identity (replaces lib's rewriteRuleForSinAndCos)+--++-- a*mr + (-a)*cos(x)^2*mr -> a*sin(x)^2*mr (since 1 - cos²x = sin²x)+declare rule auto poly $a * $mr + #(- a) * (apply1 #cos $x) ^ #2 * #mr + $pr =+  a *' (sin x)^2 *' mr +' pr++-- a*cos(x)^2*mr + b*sin(x)^2*mr -> a*mr + (b-a)*sin(x)^2*mr+-- (sin²+cos²=1 form: a*cos² + a*sin² + (b-a)*sin² = a + (b-a)*sin²)+declare rule auto poly $a * (apply1 #cos $x) ^ #2 * $mr + $b * (apply1 #sin #x) ^ #2 * #mr + $pr =+  a *' mr +' (b -' a) *' (sin x)^2 *' mr +' pr
+ sample/STATUS.md view
@@ -0,0 +1,476 @@+# sample/ 動作状況 (STATUS)++サンプルプログラムは**すべてが動くわけではありません**。本ファイルは全サンプルの現状+(型検査 `-t` での動作状況)の一覧です。サンプルや処理系を変更したら更新してください。++- 計測日: 2026-06-11(matcher rigidity 対応後の時点)+- 判定方法: `-t` は permissive(型エラーでも untyped 評価にフォールバックし exit 0)なので、+  **exit code でなく出力**で判定する。「出力に `Type error:` / `Parse error` /+  `Evaluation error` が含まれない」ことが ✅ の条件。+- timeout: math 系 90s、その他 60s(`gtimeout -k 10`)。表中 ⏱ は本基準での時間切れで、+  より長い timeout なら完走するものは備考に実績時間を記載。++```sh+# 検証コマンド(1ファイル)+gtimeout -k 10 60 cabal run -v0 egison -- -t sample/<file>.egi 2>&1 | head -20+```++## 集計++全 95 ファイル中 **62 OK**(65%)。失敗の主な内訳: 既存の型エラー+(v3 期の auto-generated サンプル等)、parse エラー(古い構文・Unicode 識別子・ハッシュ+リテラル)、実行時エラー(lib バグ・runtime dispatch)、重い計算の timeout。++> 注: 2026-06 の matcher rigidity 対応で `five-color` / `bipartite-graph` /+> `salesman` / `sat/cdcl` / `poker-hands-with-joker` / `chopsticks` /+> `generalized-sequential-pattern-mining` / `tree` / `graph` の matcher+> 引数・注釈を移行した(slot 化)。five-color は注釈整理で、bipartite-graph は+> `inductive` / `inductive pattern` 宣言の追加で型クリーン化。+> **パラメータ付き algebraicDataMatcher の正しい形** = ①データ宣言+> `inductive Edge a b := Edge a b` ②パターン宣言 `inductive pattern Edge a b := edge a b`+> ③slot 注釈付き def(`{a,b,c,d} (a: MatcherSlot b b) ... : Matcher (Edge b d)`)の3点セット。+> chopsticks / gsp / tree / salesman2 / graph / unify には別の既存エラーが残っている。++## ディレクトリ別一覧++### sample/ 直下 (15/32 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `bellman-ford.egi` | ⚠️ 実行時エラー | 1s | 実行時エラー: `Expected CASData, but found: "plus"`(typeclass 展開系) |+| `binary-counter.egi` | ✅ | 1s |  |+| `bipartite-graph.egi` | ✅ | 2s | `inductive Edge` + `inductive pattern Edge` 宣言を追加して修復(2026-06。auto-generated ファイルに宣言が欠落していた) |+| `chopsticks.egi` | ❌ 型エラー | 1s | 型エラー×11+(タプル/リスト不一致など既存の不一致に加え、ローカル assocMultiset の element-view clause `#$x :: $` が型付け不能 — stdlib 側は 2026-06-13 に同 clause を削除。design/paper-compliance-roadmap.md §4) |+| `chopsticks2.egi` | ❌ 型エラー | 2s | 型エラー(`[[Integer]]` 不一致) |+| `demo1-ja.egi` | ✅ | 1s |  |+| `demo1.egi` | ✅ | 1s |  |+| `efficient-backtracking.egi` | ⚠️ 実行時エラー | 1s | 実行時エラー: `Expected rational, but found: n` |+| `five-color.egi` | ✅ | 1s |  |+| `generalized-sequential-pattern-mining.egi` | ❌ 型エラー | 2s | 型エラー(Integer vs Float ほか。rigidity は注釈撤去で解消済み、残りは既存の型不一致) |+| `graph.egi` | ❌ parse | 1s | parse エラー (45:6, ハッシュリテラル `{|1, 4, 3|}`) |+| `ioRef.egi` | ❌ 型エラー | 1s | 型エラー(Integer 不一致) |+| `mahjong.egi` | ✅ | 43s | 型クリーン+assertion 2 件 pass(負荷によっては数十秒) |+| `mickey.egi` | ✅ | 1s |  |+| `n-queen.egi` | ❌ 型エラー | 1s | 型エラー(`n-queens.egi` は OK) |+| `n-queens.egi` | ✅ | 2s |  |+| `nishiwaki.egi` | ❌ 型エラー | 1s | 型エラー(Integer 不一致) |+| `one-minute-first.egi` | ✅ | 1s |  |+| `one-minute-second.egi` | ✅ | 7s |  |+| `pi.egi` | ⏱ timeout | 60s | 60s 超(π の桁計算、性質上重い) |+| `poker-hands-with-joker.egi` | ✅ | 13s |  |+| `poker-hands.egi` | ✅ | 3s |  |+| `prime-millionaire.egi` | ❌ 型エラー | 1s | 型エラー(`IO String` 不一致) |+| `primes.egi` | ✅ | 1s |  |+| `salesman.egi` | ✅ | 1s |  |+| `salesman2.egi` | ❌ 型エラー | 1s | 型エラー(loop+let+hash の複合パターン。`salesman.egi` は OK) |+| `tail-recursion.egi` | ⏱ timeout | 60s | 60s 超(性質上の重い計算/無限) |+| `tak.egi` | ✅ | 1s |  |+| `tree.egi` | ❌ 型エラー | 1s | `inductive Tree` + `inductive pattern Tree` 宣言を追加して前進したが、`$ :: $` / `$ ++ $` 節(リスト用パターン構築子の Tree への流用、v3 流)が型エラーのまま |+| `triangle.egi` | ❌ 型エラー | 1s | 型エラー(Integer 不一致) |+| `unify.egi` | ❌ parse | 1s | parse エラー (88:11, Unicode 識別子 `$σ`) |+| `xml-test.egi` | ⚠️ exit 1 | 1s | exit 1(原因未調査) |++### database/ (0/2 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `edge-sqlite.egi` | ❌ parse | 1s | parse エラー (7:12) |+| `simple-sqlite.egi` | ⚠️ 実行時エラー | 1s | 実行時エラー: `simpleSelect` 未定義(sqlite プリミティブ) |++### io/ (4/5 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `args.egi` | ✅ | 1s |  |+| `cat.egi` | ✅ | 1s |  |+| `cut.egi` | ❌ 型エラー | 1s | 型エラー(`IO String` 不一致) |+| `hello.egi` | ✅ | 1s |  |+| `print-primes.egi` | ✅ | 1s |  |++### physics/ (1/3 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `tension.egi` | ✅ | 4s |  |+| `tension2.egi` | ⚠️ 実行時エラー | 1s | 実行時エラー: runtime dispatch `Ord` インスタンス無し |+| `tension3.egi` | ⚠️ 実行時エラー | 1s | 実行時エラー: runtime dispatch `Ord` インスタンス無し |++### repl/ (0/1 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `egison.egi` | ❌ parse | 1s | parse エラー (5:10) |++### rosetta/ (3/4 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `abc_problem.egi` | ✅ | 3s |  |+| `consolidate.egi` | ✅ | 1s |  |+| `lcs.egi` | ❌ parse | 1s | parse エラー (5:16) |+| `partial.egi` | ✅ | 1s |  |++### sat/ (1/2 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `cdcl.egi` | ✅ | 8s |  |+| `dp.egi` | ❌ 型エラー | 2s | 型エラー(タプルパターン arity) |++### math/algebra/ (3/3 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `cubic-equation.egi` | ✅ | 5s |  |+| `quadratic-equation.egi` | ✅ | 2s |  |+| `quartic-equation.egi` | ✅ | 2s |  |++### math/analysis/ (2/3 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `eulers-formula.egi` | ✅ | 8s |  |+| `leibniz-formula.egi` | ⚠️ 実行時エラー | 1s | `Sd`(不定積分)lib バグ(下記 math 詳細参照) |+| `vector-analysis.egi` | ✅ | 5s |  |++### math/geometry/ (26/33 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `chern-form-of-CP1.egi` | ✅ | 2s |  |+| `chern-form-of-CP2.egi` | ✅ | 1s |  |+| `curvature-form.egi` | ✅ | 5s |  |+| `euler-form-of-S2.egi` | ✅ | 4s |  |+| `euler-form-of-T2.egi` | ✅ | 5s |  |+| `exterior-derivative.egi` | ✅ | 1s |  |+| `hodge-E3.egi` | ✅ | 3s |  |+| `hodge-Minkowski.egi` | ✅ | 18s |  |+| `hodge-laplacian-polar.egi` | ✅ | 2s |  |+| `hodge-laplacian-spherical.egi` | ✅ | 13s |  |+| `polar-laplacian-2d-2.egi` | ✅ | 2s |  |+| `polar-laplacian-2d-3.egi` | ✅ | 1s |  |+| `polar-laplacian-2d.egi` | ✅ | 7s |  |+| `polar-laplacian-3d-2.egi` | ✅ | 6s |  |+| `polar-laplacian-3d-3.egi` | ✅ | 5s |  |+| `polar-laplacian-3d.egi` | ✅ | 48s |  |+| `riemann-curvature-tensor-of-FLRW-metric.egi` | ✅ | 1s |  |+| `riemann-curvature-tensor-of-S2-no-type-annotations.egi` | ✅ | 5s |  |+| `riemann-curvature-tensor-of-S2.egi` | ✅ | 4s |  |+| `riemann-curvature-tensor-of-S2xS3.egi` | ⏱ timeout | 90s | 90s 超(5×5 symbolic 行列の逆行列が単独で >60s) |+| `riemann-curvature-tensor-of-S3.egi` | ✅ | 18s |  |+| `riemann-curvature-tensor-of-S4.egi` | ✅ | 43s |  |+| `riemann-curvature-tensor-of-S5-non-sym.egi` | ⏱ timeout | 90s | 90s 超(長 timeout なら完走: 実績 104s) |+| `riemann-curvature-tensor-of-S5.egi` | ⏱ timeout | 90s | 90s 超(長 timeout なら完走: 実績 104s) |+| `riemann-curvature-tensor-of-S7.egi` | ⏱ timeout | 90s | 90s 超(heavy computation) |+| `riemann-curvature-tensor-of-Schwarzschild-metric.egi` | ✅ | 12s |  |+| `riemann-curvature-tensor-of-T2-non-sym.egi` | ✅ | 10s |  |+| `riemann-curvature-tensor-of-T2.egi` | ✅ | 10s |  |+| `surface.egi` | ✅ | 4s |  |+| `thurston-non-sym.egi` | ⏱ timeout | 90s | 90s 超(+ tensor index バグ) |+| `thurston.egi` | ⏱ timeout | 90s | 90s 超(Mathematica 級の簡約が必要) |+| `wedge-product.egi` | ✅ | 1s |  |+| `yang-mills-equation-of-U1-gauge-theory.egi` | ⏱ timeout | 90s | 90s 超(長 timeout なら完走: 実績 224s) |++### math/number/ (7/7 OK)++| ファイル | 状態 | 時間 | 備考 |+|---|---|---:|---|+| `17th-root-of-unity.egi` | ✅ | 3s |  |+| `5th-root-of-unity.egi` | ✅ | 1s |  |+| `7th-root-of-unity.egi` | ✅ | 1s |  |+| `eisenstein-primes.egi` | ✅ | 2s |  |+| `euler-totient-function.egi` | ✅ | 1s |  |+| `gaussian-primes.egi` | ✅ | 3s |  |+| `tribonacci.egi` | ✅ | 2s |  |+++## 更新ガイド++- サンプルを追加・修正したら該当行を更新する(検証コマンドは上記)。+- math 系の詳細(per-file の assertion 数・失敗原因・改善ロードマップ)は下の+  「math 系サンプル詳細」を参照・更新する。+- 処理系側の変更でサンプルの挙動が変わった場合(型規則の変更等)は、変更点を+  集計の注記に一行残すこと。++---++## math 系サンプル詳細(旧 design/sample-math.md)++`sample/math/` 以下の数学サンプルプログラムは、Egison の CAS・テンソル代数・微分形式+等の表現力を実際の応用数学計算で示すショーケース集です。本ドキュメントは:++1. 全サンプルの現状(pass/fail, assertion 数)+2. 失敗の根本原因の分類+3. coverage を増やすためのロードマップ++をまとめます。**しばらくの目標は「全サンプルが意味のある assertion を持ち、すべて pass+すること」**。++---++### 集計 (最新)++| 区分 | 合計 | PASS | FAIL | assertion 平均 |+|---|---:|---:|---:|---:|+| `algebra/` | 3 | 3 | 0 | 2.0 |+| `analysis/` | 3 | 2 | 1 | 8.3 |+| `geometry/` | 33 | 28 | 5 | 5.7 |+| `number/` | 7 | 7 | 0 | 2.4 |+| **合計** | **46** | **41** | **5** | **5.4** |++PASS 率: **89% (41/46)**。assertion 総数: 約 250 個。++> **注**: per-sample timeout = 想定時間 × 1.5 (FAIL カテゴリは 300s 固定) で計測。+> `yang-mills-equation-of-U1-gauge-theory.egi` は 224s で完走するため、180s 固定+> timeout だと FAIL になる。++#### Quote semantics 修正による直近の改善 (2026-05)++`'(...)` (apostrophe) と `` `(...) `` (backtick) は **別々の機構** で、+egison-book §4.3-4.4 に正式な区別がある:++- **`` ` `` (バッククオート) — 式展開の制御** (§4.3): 続く式を一つの opaque atom+  として扱い、積和標準形展開を抑制する。`` `(x+1)^2 `` は `` `(x+1)^2 `` のまま、+  `` `(x+1)^2 / `(x+1) = `(x+1) `` のように約分される。+- **`'` (シングルクオート) — 関数適用の制御** (§4.4): **続くものは変数のみ**。+  定義済み変数を関数記号として扱う。`'sqrt`, `'sin` 等の用途。++古い sample は `'(...)` (apostrophe + 括弧式) を書いていたが、これは+**book 仕様外** で、parser の `QuoteSymbolExpr` の fallback arm で「中を+評価して quote せず返す」だけなので、**式展開抑制は効かない**。これが+polynomial blow-up の原因だった。修正は `` `(...) `` への置換 (= **書き間違い+の修正**)。++将来的に v3 era の sample を引っ張ってきた場合、同様に `'(...)` → `` `(...) ``+の置換が必要になる可能性がある。書く人は最初から `` ` `` を使うこと。++この semantics の理解が曖昧だったため複数の sample が `'(...)` 形で書かれて+**多項式 blow-up** で fail / timeout していた。`` ` `` に置換することで:++| ファイル | Before | After |+|---|---|---|+| `euler-form-of-T2.egi` | 168s で fail (degree 12 polynomial) | **7.2s で PASS** |+| `euler-form-of-S2.egi` | 古い `d` 定義で型エラー | **3.8s で PASS** (paper canonical 形に書き直し) |+| `riemann-curvature-tensor-of-Schwarzschild-metric.egi` | timeout / Christoffel 簡約失敗 | **13.7s で PASS** |+| `riemann-curvature-tensor-of-FLRW-metric.egi` | (pass済) | **3.3s で PASS** (assertion を `` ` `` 形に同期) |+| `surface.egi` | 型エラー | **4.7s で PASS** (`function (x,y)` declaration + `userRefs` style) |++> **注**: PASS/FAIL は test runner の timeout 設定に依存。30s timeout だと+> `polar-laplacian-3d` と `riemann-curvature-tensor-of-S4` が時間切れになるため、+> 60s 以上を推奨。本ドキュメントは 60s timeout 基準。++---++### 全サンプル一覧++#### `algebra/` (3/3 PASS)++| ファイル | 状態 | 時間 | asserts | 内容 |+|---|---|---:|---:|---|+| `cubic-equation.egi` | ✅ | 4s | 1 | カルダノ公式による 3 次方程式の解 |+| `quadratic-equation.egi` | ✅ | 3s | 4 | 2 次方程式の解の公式 |+| `quartic-equation.egi` | ✅ | 1s | 1 | フェラーリ公式による 4 次方程式の解 |++#### `analysis/` (1/3 PASS)++| ファイル | 状態 | 時間 | asserts | 内容 / 失敗原因 |+|---|---|---:|---:|---|+| `eulers-formula.egi` | ✅ | 8s | 4 | テイラー展開によるオイラーの公式 |+| `leibniz-formula.egi` | ❌ | 1s | 6 | π/4 のライプニッツ級数。`Sd` (不定積分) lib バグで `Sd x 'cos x` 等が `'cos x` を数として扱おうとして失敗 |+| `vector-analysis.egi` | ✅ | 6s | 15 | テイラー展開・偏微分・gradient/curl/div。`"Taylor expansion of f(x)"` の assertion を **string-equality (脆弱)** から **assertEqual on values (canonical-form 不変)** に書き換えて pass |++#### `geometry/` (17/32 PASS) — 微分幾何・物理サンプル++##### Hodge / Laplacian / 微分形式 (一部 PASS)++| ファイル | 状態 | 時間 | asserts | 内容 / 失敗原因 |+|---|---|---:|---:|---|+| `exterior-derivative.egi` | ✅ | 1s | 3 | 外微分 d、d²=0 |+| `wedge-product.egi` | ✅ | 1s | 4 | ウェッジ積の反対称性 |+| `hodge-E3.egi` | ✅ | 3s | 2 | ユークリッド 3 次元の Hodge ∗ |+| `hodge-Minkowski.egi` | ✅ | 18s | 2 | ミンコフスキー時空の Hodge ∗ |+| `hodge-laplacian-polar.egi` | ✅ | 2s | 1 | 極座標 Laplacian (Hodge 経由) |+| `hodge-laplacian-spherical.egi` | ✅ | 13s | 1 | 球座標 Laplacian。`abs(non-negative monomial)` の declare rule 追加で `abs(sin²θ r⁴) = sin²θ r⁴` が消えるようになり pass |++##### 極座標 Laplacian バリアント++| ファイル | 状態 | 時間 | asserts | 内容 / 失敗原因 |+|---|---|---:|---:|---|+| `polar-laplacian-2d.egi` | ✅ | 7s | 2 | 2D 極 Laplacian (chain rule 経由) |+| `polar-laplacian-2d-2.egi` | ✅ | 1s | 3 | 2D 極 Laplacian (テンソル記法) |+| `polar-laplacian-2d-3.egi` | ✅ | 2s | 2 | 2D 極 Laplacian (別の手法) |+| `polar-laplacian-3d.egi` | ✅ | 46s | 1 | 3D 極 Laplacian |+| `polar-laplacian-3d-2.egi` | ✅ | 6s | 2 | 3D 極 Laplacian バリアント |+| `polar-laplacian-3d-3.egi` | ✅ | 5s | 2 | 3D 極 Laplacian バリアント |++##### Riemann 曲率テンソル++| ファイル | 状態 | 時間 | asserts | 内容 / 失敗原因 |+|---|---|---:|---:|---|+| `riemann-curvature-tensor-of-S2.egi` | ✅ | 4s | 11 | S² の Riemann tensor |+| `riemann-curvature-tensor-of-S2-no-type-annotations.egi` | ✅ | 5s | 11 | S² (型注釈なし版) |+| `riemann-curvature-tensor-of-S3.egi` | ✅ | 18s | 15 | S³ |+| `riemann-curvature-tensor-of-S4.egi` | ✅ | 42s | 14 | S⁴ |+| `riemann-curvature-tensor-of-S5.egi` | ✅ | 104s | 9 | S⁵。`declare symbol` 追加で動作 |+| `riemann-curvature-tensor-of-S5-non-sym.egi` | ✅ | 104s | 7 | S⁵ (非対称 Christoffel) |+| `riemann-curvature-tensor-of-S7.egi` | ❌ | >180s | 7 | S⁷。`declare symbol` + `ε` → `ξ` rename (Levi-Civita lib 衝突回避) で型エラー解消、但しまだ heavy computation で timeout |+| `riemann-curvature-tensor-of-S2xS3.egi` | ❌ | >180s | 1 | S²×S³。`'(...)` を `` ` `` に変えても M.inverse on 5×5 symbolic matrix 単独で >60s かかり timeout |+| `riemann-curvature-tensor-of-T2.egi` | ✅ | 9s | 15 | T² (トーラス) |+| `riemann-curvature-tensor-of-T2-non-sym.egi` | ✅ | 11s | 15 | T² (非対称) |+| `riemann-curvature-tensor-of-FLRW-metric.egi` | ✅ | 1s | 2 | FLRW (宇宙論) 計量。`'(1-Kr²)` → `` `(1-Kr²) `` |+| `riemann-curvature-tensor-of-Schwarzschild-metric.egi` | ✅ | 12s | 12 | Schwarzschild 計量。`'(c²r-2GM)` → `` `(c²r-2GM) `` で完走、Christoffel assertion を CAS canonical 形に同期、Ric_ij = 0 (vacuum solution) も確認 |++##### Chern / Euler 形式++| ファイル | 状態 | 時間 | asserts | 内容 / 失敗原因 |+|---|---|---:|---:|---|+| `chern-form-of-CP1.egi` | ✅ | 2s | 3 | CP¹ の Chern 形式。multi-factor `exp` 規則の追加で `exp(-x)*exp(x)` の cancellation が 3 因子以上の項でも fire するようになり pass |+| `chern-form-of-CP2.egi` | ✅ | 1s | 1 | CP² の Chern 形式 |+| `curvature-form.egi` | ✅ | 5s | 8 | 曲率形式の一般論 |+| `euler-form-of-S2.egi` | ✅ | 3s | 2 | S² の Euler 形式。paper canonical (`!(flip ∂/∂)` の disjoint completion + `Γ~i_j_#` 1-form) に書き直し |+| `euler-form-of-T2.egi` | ✅ | 6s | 6 | T² の Euler 形式。`'(a*cos θ+b)` → `` `(...) `` で polynomial blow-up を抑制 (was 168s で fail) |++##### Thurston / その他特殊幾何++| ファイル | 状態 | 時間 | asserts | 内容 / 失敗原因 |+|---|---|---:|---:|---|+| `surface.egi` | ✅ | 4s | 10 | 一般曲面の幾何。`f` を `function (x,y)` で declare + `userRefs f [n]` 形に書き直し |+| `thurston.egi` | ❌ | >180s | 4 | Thurston 例の WCS 不変量 (EMR paper §4)。Mathematica 級の簡約が必要 |+| `thurston-non-sym.egi` | ❌ | >180s | 4 | Thurston (非対称 ∇J)。**`Inconsistent tensor index: [_?] vs [_<val>]`** バグ |+| `yang-mills-equation-of-U1-gauge-theory.egi` | ✅ | 224s | 2 | U(1) ゲージ Yang-Mills。重い計算で 180s timeout だと FAIL するが 224s で完走 |++#### `number/` (6/7 PASS)++| ファイル | 状態 | 時間 | asserts | 内容 / 失敗原因 |+|---|---|---:|---:|---|+| `5th-root-of-unity.egi` | ✅ | 2s | 2 | 1 の原始 5 乗根 (`z^5 = 1` assertion はコメントアウト — denesting 未実装) |+| `7th-root-of-unity.egi` | ✅ | 1s | 1 | 7 乗根 |+| `17th-root-of-unity.egi` | ✅ | 3s | 1 | 17 乗根 (Gauss の定規コンパス作図) |+| `eisenstein-primes.egi` | ✅ | 1s | 2 | アイゼンシュタイン素数 |+| `gaussian-primes.egi` | ✅ | 2s | 2 | ガウス素数 |+| `euler-totient-function.egi` | ✅ | 2s | 1 | オイラー totient 関数 |+| `tribonacci.egi` | ✅ | 3s | 8 | トリボナッチ。lib `M.*` (Matrix*Matrix only) は `B_j_k` の余分 index で error。sample 内で `def MV.* a b := withSymbols [i, j] (a~i~j . b_j)` を定義して使用 |++---++### 失敗原因の分類++| カテゴリ | 件数 | 関連サンプル | 修正方針 |+|---|---:|---|---|+| **CAS 簡約の弱さ** | 5+ | `vector-analysis` (Taylor), `chern-form-of-CP1` (`exp(-x)*exp(x)`), `hodge-laplacian-spherical` (`abs`), `Schwarzschild` (入れ子分数), `euler-form-of-S2`/`T2` 等 | 簡約規則の拡充 (declare rule auto): `exp(-x)*exp(x)=1`, `abs` の符号確定、入れ子分数の縮約等 |+| **型エラー** (`Cannot unify types`) | 4 | `surface`, `thurston`, `riemann-curvature-tensor-of-S7`, おそらく `S4`/`S5` も | 型推論が複合幾何構造を扱えない。要詳細調査 |+| **`Sd` (不定積分) lib バグ** | 1 | `leibniz-formula` | `lib/math/analysis/integral.egi` の `Sd` 関数のリライト |+| **タイムアウト (>30s)** | 2 | `polar-laplacian-3d`, `riemann-curvature-tensor-of-S2xS3` | テスト時の timeout 緩和 / `casRewriteDd` 高速化 |+| **テンソル index pattern バグ** | 2 | `thurston-non-sym` (`Inconsistent tensor index`), `tribonacci` (`Tensor index must be integer or single symbol`) | テンソル index 評価器の修正 |+| **詳細未捕捉** | 4 | `S4`, `S5`, `S5-non-sym`, `yang-mills`, `euler-form-of-S2`/`T2`, `polar-laplacian-3d` | 個別調査が必要 (warning のみで exit 1 になるパターン含む) |++---++### 既に取り組んだ修正の効果++直近の以下の修正で:++- **A1 (`coefficients` Frac 係数バグ)** → `quadratic-equation.egi` が pass するように+- **A2 (型 unifier widening)** → `mini-test/107` 等の widening 注釈が動くように+- **A4 (`def n : T := zero` dispatch)** → 0-arity typeclass method の typed annotation+- **B1 (multi-factor sqrt 規則)** → 3-項版 5th root of unity 等の nested radical+- **uppercase symbol pre-binding** → `FLRW` で `K` を含む式が動くように、`Schwarzschild` で `G/M/c` の算術が動くように+- **multi-factor `exp` 規則 (B1 と同じパターン)** → `exp(-x)*exp(x)` の cancellation が 3 因子以上の項でも fire するように。`chern-form-of-CP1.egi` が **NEW PASS**+- **`chern-form-of-CP1.egi` の expected value** を CAS canonical 形に合わせて更新 (`r/(-A) = -r/A`)+- **timeout 緩和**: `polar-laplacian-3d.egi` は計算 ~40s で完了するため、60s timeout なら pass++これにより **PASS 数が 27 → 31 (60% → 69%)** に改善:++| ファイル | PASS 化の理由 |+|---|---|+| `chern-form-of-CP1.egi` | multi-factor exp 規則 + expected value の sign 修正 |+| `polar-laplacian-3d.egi` | timeout 緩和 (~40s で計算完了) |+| `riemann-curvature-tensor-of-S4.egi` | timeout 緩和 (~50s で計算完了) |+| `hodge-laplacian-spherical.egi` | `abs(non-negative monomial) = monomial` 規則の追加で `abs(sin²θ r⁴)` 因子が消えるように |+| `vector-analysis.egi` | string-equality assertion を value 比較に書き直し (canonical form の order 違いに頑健に) |++---++### Coverage 改善のロードマップ++#### Phase 1: 軽量な assertion 追加 (low-hanging fruit)++既に PASS している 27 サンプルのうち、assertion が少ないものに追加する。これは+runtime バグ修正なしで coverage を上げられる。++| ファイル | 現 asserts | 追加余地 |+|---|---:|---|+| `algebra/cubic-equation.egi` | 1 | カルダノ判別式の確認、各根の平方項チェック等 |+| `algebra/quartic-equation.egi` | 1 | フェラーリ補助方程式の解 |+| `geometry/chern-form-of-CP2.egi` | 1 | 中間 connection や Chern 数の値 |+| `geometry/hodge-laplacian-polar.egi` | 1 | 中間ステップ |+| `number/17th-root-of-unity.egi` | 1 | 中間 cyclotomic 多項式値 |+| `number/7th-root-of-unity.egi` | 1 | 同上 |+| `number/euler-totient-function.egi` | 1 | 個別の n に対する φ(n) 値 |++#### Phase 2: 簡約規則の拡充 (CAS 強化)++現状の FAIL の大半は CAS 簡約の弱さ。declare rule auto の追加で複数同時に通せる+可能性が高い:++- ✅ **`exp(-x) * exp(x) = 1`** (B1 と同じ multi-factor body-match 形): `chern-form-of-CP1.egi` 等が PASS+- ✅ **`abs(positive_expr) = positive_expr`** (declare rule auto で全偶数冪 monomial 内部の `abs` を剥がす): `hodge-laplacian-spherical.egi` 等が PASS+- ⏳ **入れ子分数の縮約**: `(c²r - 2GM) / (c²r)` 形の簡約 (Schwarzschild 系)。多項式 GCD/factor が必要なので大物 (Phase 5 相当)+- ✅ **CAS 簡約 fixedpoint の到達深さ** (調査済 = NOT the issue): 残りの FAIL の原因を個別に調べた結果、iteration 不足ではなく (a) `Sd` lib バグ, (b) 型エラー, (c) Christoffel 簡約 (多項式 GCD), (d) tensor index 評価バグ, (e) タイムアウト の 5 種に分類できた。`iterateRulesLoopWithTriggers` (Primitives.hs) の fixed-point ループは正しく動作している++#### Phase 3: lib バグ修正++- **`Sd` (不定積分)** の根本書き直し → `leibniz-formula.egi` が pass++#### Phase 4: テンソル / dispatch バグ修正++- **テンソル index pattern バグ** (`Inconsistent tensor index`) → `tribonacci`, `thurston-non-sym`+- **typeclass dispatch** (`__super_AddGroup`) → `thurston`+- **EMR paper の WCS 不変量計算** が pass すれば、Egison が論文計算の参照実装として成立++#### Phase 5: 性能改善++- **`casRewriteDd` の高速化または declare rule 化** → `S2xS3`, `polar-laplacian-3d` の+  タイムアウト解消+- **Schwarzschild の Christoffel 計算** が手元で 30s 内に終わるよう最適化++#### Phase 6: 新規サンプル追加++既存サンプルが十分通るようになったら、新しい応用領域を追加:++- 量子力学(時間依存 Schrödinger 方程式の摂動展開)+- 一般相対論(Kerr, Reissner-Nordström 計量)+- 表現論(Lie 代数の構造定数計算)+- 数論(Eisenstein 級数、L 関数の解析的部分)++---++### 検証コマンド++```sh+## 全 sample/math 実行 (約 5-10 分)+for f in $(find sample/math -name "*.egi" | sort); do+  if gtimeout 60 cabal run egison -- -t "$f" >/dev/null 2>&1; then+    echo "PASS: $f"+  else+    echo "FAIL: $f"+  fi+done++## 個別 PASS/FAIL 確認+gtimeout 60 cabal run egison -- -t sample/math/<...>.egi+```++---++### 関連ドキュメント++- [type-cas.md](../design/type-cas.md) — CAS 型システム設計と既知の制限+- [type-cas-tower.md](../design/type-cas-tower.md) — 拡張可能 CAS タワー (将来)+- [function-symbol.md](../design/function-symbol.md) — 関数シンボル機構++EMR paper の Thurston 計算は `/Users/egisatoshi/PL/EMR-Paper-Computation/` に元実装。+WCS 不変量の Wolfram 簡約形は:+> `S = p² κ (-25 - 640 p² β² + 3072 p⁴ β⁴) / (16 β⁴)`  where `β = 1 + θ₂ - θ₂²`++を `sample/math/geometry/thurston.egi` の `assertEqual` で参照。
sample/bipartite-graph.egi view
@@ -2,9 +2,12 @@ -- This file has been auto-generated by egison-translator. -- -def bipartiteGraph {a, b, c, d} (a: Matcher b) (c: Matcher d) : Matcher [Edge b d] := multiset (edge a c)+inductive Edge a b := Edge a b+inductive pattern Edge a b := edge a b -def edge {a, b, c, d} (a: Matcher b) (c: Matcher d) : Matcher (Edge b d) :=+def bipartiteGraph {a, b, c, d} (a: MatcherSlot b b) (c: MatcherSlot d d) : Matcher [Edge b d] := multiset (edge a c)++def edge {a, b, c, d} (a: MatcherSlot b b) (c: MatcherSlot d d) : Matcher (Edge b d) :=   algebraicDataMatcher     | edge a c 
sample/chopsticks.egi view
@@ -1,4 +1,4 @@-def assocMultiset {a, b, c} (a: Matcher b) : Matcher [(b, Integer)] := matcher+def assocMultiset {a, b, c} (a: MatcherSlot b b) : Matcher [(b, Integer)] := matcher   | [] as () with     | [] -> [()]     | _  -> []
sample/five-color.egi view
@@ -2,9 +2,9 @@ -- This file has been auto-generated by egison-translator. -- -def node {a} : Matcher (Integer, Maybe a) := (integer, maybe integer)+def node := (integer, maybe integer) -def graph {a, b} : Matcher [((Integer, Maybe a), [(Integer, Maybe a)])] := set (node, multiset node)+def graph := set (node, multiset node)  def colors : [Integer] := between 1 5 
sample/generalized-sequential-pattern-mining.egi view
@@ -27,9 +27,9 @@ -- Utils -- -def query {a, b, c} : Matcher [(Integer, a)] := list (integer, eq)+def query := list (integer, eq) -def sequence {a, b, c} : Matcher [(Integer, [a])] := list (time, list eq)+def sequence := list (time, list eq)  def time {a, b} : Matcher Integer := matcher   | interval $ $ as (integer, integer) with
sample/graph.egi view
@@ -7,9 +7,9 @@ -- -- Matcher definition ---def graph {a, b} (a: Matcher b) : Matcher [Edge b] := set (edge a)+def graph {a, b} (a: MatcherSlot b b) : Matcher [Edge b] := set (edge a) -def edge {a, b} (a: Matcher b) : Matcher (Edge b) :=+def edge {a, b} (a: MatcherSlot b b) : Matcher (Edge b) :=   algebraicDataMatcher     | edge a a 
sample/mahjong.egi view
@@ -65,16 +65,18 @@ -- def complete? : [Tile] -> Bool :=   \match as multiset tile with-    | pair-        $th_1-        (sequence $sh_1-           (sequence $sh_2-              (sequence $sh_3 (sequence $sh_4 [] | triplet $kh_1 [])-                | triplet $kh_1 (triplet $kh_2 []))-             | triplet $kh_1 (triplet $kh_2 (triplet $kh_3 [])))-          | triplet $kh_1 (triplet $kh_2 (triplet $kh_3 (triplet $kh_4 []))))-        | (pair $th_2 (pair $th_3 (pair $th_4 (pair $th_5 (pair $th_6 (pair $th_7 []))))))-    -> True+    -- 1 pair (eyes) + 4 sequences+    | pair $th_1 (sequence $sh_1 (sequence $sh_2 (sequence $sh_3 (sequence $sh_4 [])))) -> True+    -- 1 pair + 3 sequences + 1 triplet+    | pair $th_1 (sequence $sh_1 (sequence $sh_2 (sequence $sh_3 (triplet $kh_1 [])))) -> True+    -- 1 pair + 2 sequences + 2 triplets+    | pair $th_1 (sequence $sh_1 (sequence $sh_2 (triplet $kh_1 (triplet $kh_2 [])))) -> True+    -- 1 pair + 1 sequence + 3 triplets+    | pair $th_1 (sequence $sh_1 (triplet $kh_1 (triplet $kh_2 (triplet $kh_3 [])))) -> True+    -- 1 pair + 4 triplets+    | pair $th_1 (triplet $kh_1 (triplet $kh_2 (triplet $kh_3 (triplet $kh_4 [])))) -> True+    -- 7 pairs (seven-pairs hand)+    | pair $th_1 (pair $th_2 (pair $th_3 (pair $th_4 (pair $th_5 (pair $th_6 (pair $th_7 [])))))) -> True     | _ -> False  --
+ sample/math/algebra/canonical-form-absorption.egi view
@@ -0,0 +1,44 @@+--+-- Canonical-form selection by type annotations, and the absorption law+--+-- One value, several canonical forms: annotations select among the flat+-- form over Z[i, x] and nested forms with Z[i] (or Z[x]) coefficients.+-- Promotion/reshape is path-independent (the absorption law of the+-- extensible-tower design, design/type-cas-tower.md D5):+--+--   reshape_C (reshape_B v) = reshape_C v+--+-- and arithmetic always exits in the default flat form, so terms coming+-- from different representations merge (i + (-i) = 0 across forms).+--++declare symbol x++def v := (2 + 3*i) + (1 - i)*x + 4*x^2++-- one value, three canonical forms+def flat : Poly Integer [i, x] := v+def byX : Poly (Poly Integer [i]) [x] := v      -- coefficients in Z[i]+def byI : Poly (Poly Integer [x]) [i] := v      -- coefficients in Z[x]++assertEqual "forms agree semantically (flat vs byX)" ((flat - byX) = 0) True+assertEqual "forms agree semantically (flat vs byI)" ((flat - byI) = 0) True+assertEqual "annotation selects the nested form" (typeOf byX) "Poly (Poly Integer [i]) [x]"++-- absorption: stacking annotations equals annotating once+assertEqual "absorb: (byX then flat) = flat"+  (show (byX : Poly Integer [i, x])) (show flat)+assertEqual "absorb: (flat then byX) = byX"+  (show (flat : Poly (Poly Integer [i]) [x])) (show byX)+assertEqual "absorb: cross-nesting (byX then byI) = byI"+  (show (byX : Poly (Poly Integer [x]) [i])) (show byI)+assertEqual "round trip byX -> flat -> byX"+  (show ((byX : Poly Integer [i, x]) : Poly (Poly Integer [i]) [x])) (show byX)++-- arithmetic exits in the default flat form, even on nested operands,+-- so cross-representation terms merge+assertEqual "op exit is flat" (typeOf (byX + 0)) "Poly Integer [i, x]"+assertEqual "cross-representation cancellation"+  ((byX - flat) = 0) True+def onlyI : Poly (Poly Integer [i]) [x] := i+assertEqual "i + (-i) = 0 across forms" ((onlyI + (0 - i)) = 0) True
sample/math/algebra/cubic-equation.egi view
@@ -2,14 +2,14 @@  declare symbol x, a, b, c, d, p, q -def cubicFormula : MathExpr -> MathExpr -> (MathExpr, MathExpr, MathExpr) := cF+def cubicFormula : MathValue -> MathValue -> (MathValue, MathValue, MathValue) := cF -def cF (f: MathExpr) (x: MathExpr) : (MathExpr, MathExpr, MathExpr) :=-  match coefficients f x as list mathExpr with+def cF (f: MathValue) (x: MathValue) : (MathValue, MathValue, MathValue) :=+  match coefficients f x as list mathValue with     | [$a_0, $a_1, $a_2, $a_3] -> cF' a_3 a_2 a_1 a_0 -def cF' (a: MathExpr) (b: MathExpr) (c: MathExpr) (d: MathExpr) : (MathExpr, MathExpr, MathExpr) :=-  match (a, b, c, d) as (mathExpr, mathExpr, mathExpr, mathExpr) with+def cF' (a: MathValue) (b: MathValue) (c: MathValue) (d: MathValue) : (MathValue, MathValue, MathValue) :=+  match (a, b, c, d) as (mathValue, mathValue, mathValue, mathValue) with     | (#1, #0, $p, $q) ->       let (s1, s2) := (2)#(rt 3 $1, rt 3 $2) (qF' 1 (27 * q) ((-27) * p ^ 3))        in ((s1 + s2) / 3, (w ^ 2 * s1 + w * s2) / 3, (w * s1 + w ^ 2 * s2) / 3)@@ -19,7 +19,7 @@           cF (substitute [(x, y - b / 3)] (x ^ 3 + b * x ^ 2 + c * x + d)) y)     | (_, _, _, _) -> cF' 1 (b / a) (c / a) (d / a) -def w : MathExpr := ((-1) + i * sqrt 3) / 2+def w : MathValue := ((-1) + i * sqrt 3) / 2  -- Solution for x^3 + p*x + q = 0 (depressed cubic) (3)#$1 (cF (x ^ 3 + p * x + q) x)
+ sample/math/algebra/groebner-basis.egi view
@@ -0,0 +1,106 @@+--+-- Groebner bases and polynomial normal forms+--+-- The rewrite rules behind Egison's symbol-carried quotients+-- (`declare rule auto term i^2 = -1` and friends) are Groebner-basis+-- elements read as rules `LT -> -rest`.  `groebnerBasis` computes+-- the reduced basis of an ideal with Buchberger's algorithm, i.e. it+-- COMPLETES a user-written generator set into a terminating,+-- confluent rule set; `polyNF` reduces an expression to its unique+-- normal form modulo a basis, which also decides ideal membership+-- (design/cas-simplification.md, Section 3.3).+--+-- The engine is written in Egison itself on top of the poly/term+-- matchers: lib/math/algebra/groebner.egi.+--++declare symbol s2, s3, s6      -- stand-ins for sqrt 2, sqrt 3, sqrt 6+declare symbol x, y, z+declare symbol θ++--+-- 1. Completing a rule set: the multiplication table of {1, √2, √3, √6}+--+-- The user writes the three obvious relations; Buchberger completes+-- them to the full multiplication table, including the products a+-- human forgets to write down.+--++def gb := groebnerBasis [s2^2 - 2, s3^2 - 3, s6 - s2 * s3]++assertEqual "multiplication table (6 rules)"+  (show gb)+  "[s2^2 - 2, s2 s3 - s6, s2 s6 - 2 * s3, s3^2 - 3, s3 s6 - 3 * s2, s6^2 - 6]"++-- the rule nobody writes by hand: √2·√6 = 2√3+assertEqual "sqrt 2 * sqrt 6 = 2 * sqrt 3" (polyNF gb (s2 * s6)) (2 * s3)++--+-- 2. Normal forms: canonical representatives and zero recognition+--++-- (√2 + √3)^2 = 5 + 2√6+assertEqual "(s2 + s3)^2" (polyNF gb ((s2 + s3)^2)) (2 * s6 + 5)++-- ideal membership: NF = 0  <=>  the value is a consequence of the relations+assertEqual "membership" (polyNF gb (s2 * s3 - s6)) 0++-- the normal form does not change when ideal multiples are added+assertEqual "NF mod ideal shifts"+  (polyNF gb ((s2 + s3)^2 + (s2^2 - 2) * (s6 + 7)))+  (polyNF gb ((s2 + s3)^2))++-- and it is idempotent+assertEqual "NF idempotent"+  (polyNF gb (polyNF gb ((s2 + s3)^2)))+  (polyNF gb ((s2 + s3)^2))++--+-- 3. A textbook example: cyclic-3+--++def cyc := groebnerBasis [x + y + z, x*y + y*z + z*x, x*y*z - 1]++assertEqual "cyclic-3 reduced basis"+  (show cyc) "[z + y + x, y^2 + x^2 + x y, x^3 - 1]"++-- x^3 = 1 modulo the ideal, so x^5 y collapses to x^2 y+assertEqual "x^5 y mod cyclic-3" (polyNF cyc (x^5 * y)) (x^2 * y)++--+-- 4. The variable priority list: atoms listed earlier survive+--+-- `polyNFWith` (and `groebnerBasisWith`) take the atoms to KEEP as a+-- prefix; every other atom ranks higher and gets eliminated first.+--++assertEqual "keep y" (polyNFWith [y] [x + y] (x^3)) (- y^3)+assertEqual "keep x" (polyNFWith [x] [x + y] (y^3)) (- x^3)++--+-- 5. Trigonometric atoms, no change of variables+--+-- `sin θ` and `cos θ` are factors of the flat representation, so the+-- Pythagorean ideal works on them directly.  `trigIdeal` (defined in+-- lib/math/algebra/groebner.egi) builds the generator inside the+-- rule-suppression quote '( ): a plain `(sin θ)^2 + (cos θ)^2 - 1`+-- would be collapsed to 0 by the built-in Pythagorean auto rules+-- before polyNF ever sees it.+--++assertEqual "the generator survives construction"+  (show (trigIdeal θ)) "['sin θ^2 + 'cos θ^2 - 1]"++-- keep sin θ: rewrite everything to a polynomial in sin θ+assertEqual "sin^4 - cos^4"+  (polyNFWith [('sin θ)] (trigIdeal θ) ((sin θ)^4 - (cos θ)^4))+  (2 * (sin θ)^2 - 1)++assertEqual "cos^6"+  (polyNFWith [('sin θ)] (trigIdeal θ) ((cos θ)^6))+  ((1 - (sin θ)^2)^3)++-- membership works independently of the chosen direction+assertEqual "trig membership"+  (polyNF (trigIdeal θ) ('((sin θ)^2 + (cos θ)^2 - 1) * (3 + sin θ)))+  0
sample/math/algebra/quadratic-equation.egi view
@@ -2,14 +2,14 @@  declare symbol x, a, b, c -def quadraticFormula : MathExpr -> MathExpr -> (MathExpr, MathExpr) := qF+def quadraticFormula : MathValue -> MathValue -> (MathValue, MathValue) := qF -def qF (f: MathExpr) (x: MathExpr) : (MathExpr, MathExpr) :=-  match coefficients f x as list mathExpr with+def qF (f: MathValue) (x: MathValue) : (MathValue, MathValue) :=+  match coefficients f x as list mathValue with     | [$a_0, $a_1, $a_2] -> qF' a_2 a_1 a_0 -def qF' (a: MathExpr) (b: MathExpr) (c: MathExpr) : (MathExpr, MathExpr) :=-  match (a, b, c) as (mathExpr, mathExpr, mathExpr) with+def qF' (a: MathValue) (b: MathValue) (c: MathValue) : (MathValue, MathValue) :=+  match (a, b, c) as (mathValue, mathValue, mathValue) with     | (#1, #0, _) -> (sqrt (- c), - sqrt (- c))     | (#1, _, _) ->       (2)#((- (b / 2)) + $1, (- (b / 2)) + $2)
sample/math/algebra/quartic-equation.egi view
@@ -2,15 +2,15 @@  declare symbol x, y -def quarticFormula : MathExpr -> MathExpr -> (MathExpr, MathExpr, MathExpr, MathExpr) := qtF+def quarticFormula : MathValue -> MathValue -> (MathValue, MathValue, MathValue, MathValue) := qtF -def qtF (f: MathExpr) (x: MathExpr) : (MathExpr, MathExpr, MathExpr, MathExpr) :=-  match coefficients f x as list mathExpr with+def qtF (f: MathValue) (x: MathValue) : (MathValue, MathValue, MathValue, MathValue) :=+  match coefficients f x as list mathValue with     | $a_0 :: $a_1 :: $a_2 :: $a_3 :: $a_4 :: [] -> qtF' a_4 a_3 a_2 a_1 a_0 -def qtF' (a: MathExpr) (b: MathExpr) (c: MathExpr) (d: MathExpr) (e: MathExpr) : (MathExpr, MathExpr, MathExpr, MathExpr) :=+def qtF' (a: MathValue) (b: MathValue) (c: MathValue) (d: MathValue) (e: MathValue) : (MathValue, MathValue, MathValue, MathValue) :=   match (a, b, c, d, e) as-    (mathExpr, mathExpr, mathExpr, mathExpr, mathExpr) with+    (mathValue, mathValue, mathValue, mathValue, mathValue) with     | (#1, #0, $p, #0, $q) ->       let (s1, s2) := qF' 1 p q           (r1, r2) := qF' 1 0 (- s1)
sample/math/analysis/eulers-formula.egi view
@@ -1,6 +1,6 @@ -- Euler's formula: e^(ix) = cos(x) + i*sin(x) -declare symbol x : MathExpr+declare symbol x : MathValue  assertEqual "Taylor expansion of e^(ix)"   (take 8 (taylorExpansion (e^(i * x)) x 0))
sample/math/analysis/leibniz-formula.egi view
@@ -2,9 +2,9 @@ -- Leibniz formula (Fourier series coefficients) -- -def f (x: MathExpr) : MathExpr := x+def f (x: MathValue) : MathValue := x -def multSd (x: MathExpr) (f: MathExpr) (G: MathExpr) : MathExpr :=+def multSd (x: MathValue) (f: MathValue) (G: MathValue) : MathValue :=   let F := Sd x f    in F * G - Sd x (f * d/d G x) @@ -22,7 +22,7 @@   (- x * cos x + sin x + cos x)  -- Fourier coefficients for f(x) = x-def coeffAs : [MathExpr] :=+def coeffAs : [MathValue] :=   map     (\n ->       let F := multSd x (cos (n * x)) (f x)@@ -34,7 +34,7 @@   (take 10 coeffAs)   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] -def bs : [MathExpr] :=+def bs : [MathValue] :=   map     (\n ->       let F := multSd x (sin (n * x)) (f x)@@ -45,7 +45,7 @@   (take 10 bs)   [2, -1, 2/3, -1/2, 2/5, -1/3, 2/7, -1/4, 2/9, -1/5] -def f' : [MathExpr] := map (\(k, b) -> b * sin (k * x)) (zip nats bs)+def f' : [MathValue] := map (\(k, b) -> b * sin (k * x)) (zip nats bs)  -- Fourier series terms assertEqual "first 10 Fourier series terms"
sample/math/analysis/vector-analysis.egi view
@@ -87,6 +87,17 @@   (∂/∂ f3 x + ∂/∂ g3 y + ∂/∂ h3 z)  --+-- Curl (uses rot from lib/math/algebra/vector.egi)+-- Standard convention: (rot A)_i = eps_ijk d(A_k)/d(x_j),+-- i.e. (rot A)_1 = dA_3/dx_2 - dA_2/dx_3 = dh/dy - dg/dz.+--+assertEqual "curl"+  (rot [| f3, g3, h3 |] [| x, y, z |])+  [| ∂/∂ h3 y - ∂/∂ g3 z,+     ∂/∂ f3 z - ∂/∂ h3 x,+     ∂/∂ g3 x - ∂/∂ f3 y |]++-- -- Taylor Expansion -- def multivariateTaylorExpansion fexpr xs ys :=@@ -103,8 +114,20 @@  def taylorExpansion fexpr x a := multivariateTaylorExpansion fexpr [|x|] [|a|] -assert "Taylor expansion of f(x)"-  (show (take 3 (taylorExpansion f1 x 0)) = "[f1 0, x * f1|1 0, x^2 * f1|1|1 0 / 2]")+-- Compare values directly. The CAS canonical form puts coefficients first+-- and uses commutative ordering, so the printed string differs from the+-- handwritten form (e.g. `x * f|1 0` becomes `(f|1 0) * x`).+assertEqual "Taylor expansion of f(x)"+  (take 3 (taylorExpansion f1 x 0))+  [(userRefs f1 []) 0,+   (userRefs f1 [1]) 0 * x,+   (userRefs f1 [1, 1]) 0 * x^2 / 2] -assert "Multivariate Taylor expansion"-  (show (take 3 (multivariateTaylorExpansion f2 [| x, y |] [| 0, 0 |])) = "[f2 0 0, x * f2|1 0 0 + y * f2|2 0 0, (x^2 * f2|1|1 0 0 + x * y * f2|2|1 0 0 + y * x * f2|1|2 0 0 + y^2 * f2|2|2 0 0) / 2]")+assertEqual "Multivariate Taylor expansion"+  (take 3 (multivariateTaylorExpansion f2 [| x, y |] [| 0, 0 |]))+  [(userRefs f2 []) 0 0,+   (userRefs f2 [1]) 0 0 * x + (userRefs f2 [2]) 0 0 * y,+   ((userRefs f2 [1, 1]) 0 0 * x^2+    + (userRefs f2 [2, 1]) 0 0 * x * y+    + (userRefs f2 [1, 2]) 0 0 * x * y+    + (userRefs f2 [2, 2]) 0 0 * y^2) / 2]
sample/math/geometry/chern-form-of-CP1.egi view
@@ -9,7 +9,7 @@ def u := r * e ^ (2 * π * i * θ) def ū := r * e ^ ((-2) * π * i * θ) -def d (X : MathExpr) : DiffForm MathExpr := !(flip ∂/∂) params X+def d (X : MathValue) : DiffForm MathValue := !(flip ∂/∂) params X  -- Connection 1-form def ω := ū * d u / '(1 + u * ū)@@ -32,10 +32,13 @@ -- First Chern class def c1Form := Ω / ((-2) * π * i) +-- After CAS normalization, the sign goes to the numerator (denominator+-- becomes the canonical positive form):+--   r / (-1 - 2r² - r⁴) = -r / (1 + 2r² + r⁴) assertEqual "c1"   c1Form-  [| [| 0, r / ((-1) - 2 * r^2 - r^4) |]-   , [| (-1) * r / ((-1) - 2 * r^2 - r^4), 0 |] |]+  [| [| 0, (- r) / (1 + 2 * r^2 + r^4) |]+   , [| r / (1 + 2 * r^2 + r^4), 0 |] |]  -- Integration check: -- ∫∫ c1 dr dθ = ∫₀^∞ ∫₀^¹ (-2r)/(1+r²)² dθ dr
sample/math/geometry/chern-form-of-CP2.egi view
@@ -8,10 +8,10 @@ declare symbol z1, z2, z1b, z2b  -- Holomorphic exterior derivative (∂)-def dh (X : MathExpr) : DiffForm MathExpr := !(flip ∂/∂) [| z1, z2 |] X+def dh (X : MathValue) : DiffForm MathValue := !(flip ∂/∂) [| z1, z2 |] X  -- Anti-holomorphic exterior derivative (∂̄)-def da (X : MathExpr) : DiffForm MathExpr := !(flip ∂/∂) [| z1b, z2b |] X+def da (X : MathValue) : DiffForm MathValue := !(flip ∂/∂) [| z1b, z2b |] X  def h := 1 + z1 * z1b + z2 * z2b 
sample/math/geometry/curvature-form.egi view
@@ -1,18 +1,18 @@-declare symbol r, θ, φ: MathExpr+declare symbol r, θ, φ: MathValue  -- Parameters and metric tensor-def x : Vector MathExpr := [| θ, φ |]+def x : Vector MathValue := [| θ, φ |] -def g_i_j : Matrix MathExpr := [| [| r^2, 0 |], [| 0, r^2 * (sin θ)^2 |] |]_i_j-def g~i~j : Matrix MathExpr := [| [| 1 / r^2, 0 |], [| 0, 1 / (r^2 * (sin θ)^2) |] |]~i~j+def g_i_j : Matrix MathValue := [| [| r^2, 0 |], [| 0, r^2 * (sin θ)^2 |] |]_i_j+def g~i~j : Matrix MathValue := [| [| 1 / r^2, 0 |], [| 0, 1 / (r^2 * (sin θ)^2) |] |]~i~j  -- Christoffel symbols-def Γ_j_l_k : Tensor MathExpr := (1 / 2) * (∂/∂ g_j_l x~k + ∂/∂ g_j_k x~l - ∂/∂ g_k_l x~j)+def Γ_j_l_k : Tensor MathValue := (1 / 2) * (∂/∂ g_j_l x~k + ∂/∂ g_j_k x~l - ∂/∂ g_k_l x~j) -def Γ~i_k_l : Tensor MathExpr := withSymbols [j] g~i~j . Γ_j_l_k+def Γ~i_k_l : Tensor MathValue := withSymbols [j] g~i~j . Γ_j_l_k  -- Riemann curvature-def R~i_j_k_l : Tensor MathExpr := withSymbols [m]+def R~i_j_k_l : Tensor MathValue := withSymbols [m]   ∂/∂ Γ~i_j_l x~k - ∂/∂ Γ~i_j_k x~l + Γ~m_j_l . Γ~i_m_k - Γ~m_j_k . Γ~i_m_l  assertEqual "Riemann curvature" R~#_#_1_1 [| [| 0, 0 |], [| 0, 0 |] |]~#_#@@ -21,13 +21,13 @@ assertEqual "Riemann curvature" R~#_#_2_2 [| [| 0, 0 |], [| 0, 0 |] |]~#_#  -- Exterior derivative-def d (t : Tensor MathExpr) : Tensor MathExpr := !(flip ∂/∂) x t+def d (t : Tensor MathValue) : Tensor MathValue := !(flip ∂/∂) x t  -- Connection form-def ω~i_j : Matrix MathExpr := Γ~i_j_#+def ω~i_j : Matrix MathValue := Γ~i_j_#  -- Curvature form-def Ω~i_j : Tensor MathExpr := withSymbols [k]+def Ω~i_j : Tensor MathValue := withSymbols [k]   antisymmetrize (d ω~i_j + ω~i_k ∧ ω~k_j)  assertEqual "Curvature form" Ω~#_#_1_1 [| [| 0, 0 |], [| 0, 0 |] |]~#_#
sample/math/geometry/euler-form-of-S2.egi view
@@ -1,53 +1,53 @@-declare symbol r, θ, φ: MathExpr+declare symbol r, θ, φ: MathValue  -- Euler form of S2 -def x : Vector MathExpr := [| θ, φ |]+def x : Vector MathValue := [| θ, φ |] -def X : Vector MathExpr := [| r * sin θ * cos φ, r * sin θ * sin φ, r * cos θ |]+def X : Vector MathValue := [| r * sin θ * cos φ, r * sin θ * sin φ, r * cos θ |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensor-def g_i_j : Matrix MathExpr := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [2, 2]-def g~i~j : Matrix MathExpr := M.inverse g_#_#+def g_i_j : Matrix MathValue := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [2, 2]+def g~i~j : Matrix MathValue := M.inverse g_#_# -g_#_# assertEqual "Metric tensor"   g_#_#   [| [| r^2, 0 |], [| 0, r^2 * (sin θ)^2 |] |]_#_#  -- Christoffel symbols-def Γ_i_j_k : Tensor MathExpr := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i)+def Γ_i_j_k : Tensor MathValue := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i) -def Γ~i_j_k : Tensor MathExpr := withSymbols [m]+def Γ~i_j_k : Tensor MathValue := withSymbols [m]   g~i~m . Γ_m_j_k --- Connection 1-form-def ω0 : Tensor MathExpr := Γ~#_#_#--def A : Matrix MathExpr := [| [| 1 / r, 0 |], [| 0, 1 / (r * sin θ) |] |]---- Transformed connection-def d (A : Tensor MathExpr) : Tensor MathExpr := (flip ∂/∂) x~# A_#_#+-- Vielbein (orthonormal frame)+def A : Matrix MathValue := [| [| 1 / r, 0 |], [| 0, 1 / (r * sin θ) |] |] -def ω := withSymbols [i, j, k, l]-  (M.inverse A)~i_j . ω0~j_k . A~k_l + (M.inverse A)~i_j . d A~j_l+-- Exterior derivative (paper canonical: !(flip ∂/∂) x t with disjoint index completion)+def d (t : Tensor MathValue) : Tensor MathValue := !(flip ∂/∂) x t --- Curvature form-def wedge {Num a} (X : Tensor a) (Y : Tensor a) : Tensor a := X !. Y+-- Connection 1-form in coordinate basis (paper canonical: ω~i_j := Γ~i_j_#)+def ω0~i_j : Matrix MathValue := Γ~i_j_# -wedge ω~i_k ω~k_j+-- Connection 1-form in orthonormal basis (Cartan transformation):+--   ω = A⁻¹ ω₀ A + A⁻¹ dA+def ω~i_j : Tensor MathValue := withSymbols [a, b]+  (M.inverse A)~i_a . ω0~a_b . A~b_j + (M.inverse A)~i_a . d A~a_j -def Ω : Tensor MathExpr := withSymbols [i, j, k]---  dfNormalize (d ω~i_j + wedge ω~i_k ω~k_j)-  (d ω~i_j + wedge ω~i_k ω~k_j)+-- Curvature 2-form (Cartan structure equation)+def Ω~i_j : Tensor MathValue := withSymbols [k]+  antisymmetrize (d ω~i_j + ω~i_k ∧ ω~k_j) --- Euler form-def eulerForm : MathExpr := (1 / (2 * π)) * (Ω~1_2 - Ω~2_1)+-- Euler form: e(S²) = (1/(2π)) (Ω₁² - Ω²₁)+-- The withSymbols on the form indices is needed so the binary minus+-- aligns the two rank-2 form components correctly.+def eulerForm : Tensor MathValue :=+  (1 / (2 * π)) * withSymbols [t1, t2] (Ω~1_2_t1_t2 - Ω~2_1_t1_t2)  -- The Euler form integrates to the Euler characteristic χ = 2 for S² assertEqual "Euler form of S2"   eulerForm-  [| [| sin θ / (r^2 * π), 0 |], [| 0, sin θ / (r^2 * π) |] |]+  [| [| 0, sin θ / (2 * π) |], [| - sin θ / (2 * π), 0 |] |]
sample/math/geometry/euler-form-of-T2.egi view
@@ -1,50 +1,67 @@ -- Euler form of T2 (Torus) -declare symbol θ, φ, a, b+declare symbol θ, φ, a, b : MathValue -def x := [| θ, φ |]+def x : Vector MathValue := [| θ, φ |] -def X := [| '(a * cos θ + b) * cos φ, '(a * cos θ + b) * sin φ, a * sin θ |]+-- Use backtick `(...)` for opaque-atom quoting (so `(a*cos θ + b)^k stays+-- unexpanded). Apostrophe `'(...)` is the rule-suppression quote — it only+-- turns off `declare rule` rewriting during construction, it does not make+-- an opaque atom — so it would let intermediate computations explode the+-- polynomial.+def X : Vector MathValue := [| `(a * cos θ + b) * cos φ, `(a * cos θ + b) * sin φ, a * sin θ |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensor-def g_i_j := generateTensor (\[a, b] -> V.* e_a e_b) [2, 2]-def g~i~j := M.inverse g_#_#+def g_i_j : Matrix MathValue := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [2, 2]+def g~i~j : Matrix MathValue := M.inverse g_#_#  assertEqual "Metric tensor"   g_#_#-  [| [| a^2, 0 |], [| 0, '(a * cos θ + b)^2 |] |]_#_#+  [| [| a^2, 0 |], [| 0, `(a * cos θ + b)^2 |] |]_#_#  -- Christoffel symbols-def Γ_i_j_k := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i)+def Γ_i_j_k : Tensor MathValue := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i) -def Γ~i_j_k := withSymbols [m]+def Γ~i_j_k : Tensor MathValue := withSymbols [m]   g~i~m . Γ_m_j_k --- Connection 1-form-def ω0 := Γ~#_#_#+-- Vielbein (orthonormal frame)+def A : Matrix MathValue := [| [| 1 / a, 0 |], [| 0, 1 / `(a * cos θ + b) |] |] --- Vielbein-def A := [| [| 1 / a, 0 |], [| 0, 1 / '(a * cos θ + b) |] |]+-- Exterior derivative (paper canonical: !(flip ∂/∂) x t with disjoint completion)+def d (t : Tensor MathValue) : Tensor MathValue := !(flip ∂/∂) x t --- Transformed connection-def d A := (flip ∂/∂) x~# A_#_#+-- Connection 1-form in coordinate basis (paper canonical: ω~i_j := Γ~i_j_#)+def ω0~i_j : Matrix MathValue := Γ~i_j_# -def ω := withSymbols [i, j, k, l]-  (M.inverse A)~i_j . ω0~j_k . A~k_l + (M.inverse A)~i_j . d A~j_l+-- Connection 1-form in orthonormal basis (Cartan transformation):+--   ω = A⁻¹ ω₀ A + A⁻¹ dA+def ω~i_j : Tensor MathValue := withSymbols [u, v]+  (M.inverse A)~i_u . ω0~u_v . A~v_j + (M.inverse A)~i_u . d A~u_j --- Curvature form-def wedge X Y := X !. Y+-- Component-wise checks (whole-vector compare currently mismatches on+-- index metadata even when values agree, so use element access).+assertEqual "ω~1_2_1" ω~1_2_1 0+assertEqual "ω~1_2_2" ω~1_2_2 ('sin θ)+assertEqual "ω~2_1_1" ω~2_1_1 0+assertEqual "ω~2_1_2" ω~2_1_2 (- ('sin θ)) -def Ω := withSymbols [i, j, k]-  dfNormalize (d ω~i_j + wedge ω~i_k ω~k_j)+-- Curvature 2-form (Cartan structure equation)+def Ω'~i_j : Tensor MathValue := withSymbols [k]+  d ω~i_j + ω~i_k ∧ ω~k_j+def Ω~i_j_t1_t2 : Tensor MathValue := Ω'~i_j_t1_t2 - Ω'~i_j_t2_t1 --- Euler form-def eulerForm := (1 / (2 * π)) * (Ω~1_2 - Ω~2_1)+-- Euler form: e(T²) = (1/(4π)) (Ω₁² - Ω²₁)+def eulerForm : Tensor MathValue :=+  (1 / (4 * π)) * withSymbols [t1, t2] (Ω~1_2_t1_t2 - Ω~2_1_t1_t2) --- The Euler form integrates to the Euler characteristic χ = 0 for T²+-- The Euler form integrates to χ(T²) = 0.+-- In the orthonormal vielbein frame, the (a*(b+a*cos θ)) Jacobian factors+-- cancel out and the curvature 2-form reduces to cos θ /(2π) dθ∧dφ. assertEqual "Euler form of T2"   eulerForm-  [| [| cos θ / ('(a * cos θ + b) * a * π), 0 |], [| 0, cos θ / ('(a * cos θ + b) * a * π) |] |]+  [| [| 0, cos θ / (2 * π) |]+   , [| - cos θ / (2 * π), 0 |] |]
sample/math/geometry/exterior-derivative.egi view
@@ -2,23 +2,31 @@ -- Exterior Derivative -- -declare symbol x, y, z : MathExpr+declare symbol x, y, z : MathValue  def N : Integer := 3 -def params : Vector MathExpr := [|x, y, z|]+def params : Vector MathValue := [|x, y, z|]  def g : Matrix Integer := [|[|1, 0, 0|], [|0, 1, 0|], [|0, 0, 1|]|]  def d {a} (X: a) : DiffForm a := !(flip ∂/∂) params X  ---def f : MathExpr := function (x, y, z)+--def f : MathValue := function (x, y, z) def f := x ^ 2 + y ^ 2 + z ^ 2  -- The exterior derivative of f is the gradient 1-form-d f+assertEqual "d f = grad(x^2+y^2+z^2)"+  (d f)+  [| 2 * x, 2 * y, 2 * z |] --- The exterior derivative of d(f) is 0 (d^2 = 0)-d (d f)-dfNormalize (d (d f))+-- d (d f) before antisymmetrization is the Hessian matrix+assertEqual "d (d f) = Hessian (raw, pre-normalize)"+  (d (d f))+  [| [| 2, 0, 0 |], [| 0, 2, 0 |], [| 0, 0, 2 |] |]++-- After antisymmetrization (the diff-form normalizer), d^2 = 0+assertEqual "dfNormalize (d (d f)) = 0 (d^2 = 0)"+  (dfNormalize (d (d f)))+  [| [| 0, 0, 0 |], [| 0, 0, 0 |], [| 0, 0, 0 |] |]
sample/math/geometry/hodge-E3.egi view
@@ -14,8 +14,8 @@         sqrt (abs (M.det g_#_#)) *         foldl           (.)-          ((subrefs A (map 1#j_$1 (between 1 k))) . (subrefs (ε' N k) (map 1#i_$1 (between 1 N))))-          (map (\n -> g~(i_n)~(j_n)) (between 1 k))+          ((ε' N k)_(i_1)..._(i_N) . A..._(j_1)..._(j_k))+          (map (\n -> g~(i_n)~(j_n)) [1..k])  def dx := [|1, 0, 0|] def dy := [|0, 1, 0|]
sample/math/geometry/hodge-Minkowski.egi view
@@ -4,23 +4,23 @@  def N : Integer := 4 -def params : Vector MathExpr := [|t, x, y, z|]+def params : Vector MathValue := [|t, x, y, z|] -def g : Matrix MathExpr := [|[|-1, 0, 0, 0|], [|0, 1, 0, 0|], [|0, 0, 1, 0|], [|0, 0, 0, 1|]|]+def g : Matrix MathValue := [|[|-1, 0, 0, 0|], [|0, 1, 0, 0|], [|0, 0, 1, 0|], [|0, 0, 0, 1|]|] -def hodge (A: DiffForm MathExpr) : DiffForm MathExpr :=+def hodge (A: DiffForm MathValue) : DiffForm MathValue :=   let k := dfOrder A    in withSymbols [i, j]         sqrt (abs (M.det g_#_#)) *         foldl           (.)-          ((subrefs A (map 1#j_$1 (between 1 k))) . (subrefs (ε' N k) (map 1#i_$1 (between 1 N))))-          (map (\n -> g~(i_n)~(j_n)) (between 1 k))+          ((ε' N k)_(i_1)..._(i_N) . A..._(j_1)..._(j_k))+          (map (\n -> g~(i_n)~(j_n)) [1..k]) -def dt : DiffForm MathExpr := [|1, 0, 0, 0|]-def dx : DiffForm MathExpr := [|0, 1, 0, 0|]-def dy : DiffForm MathExpr := [|0, 0, 1, 0|]-def dz : DiffForm MathExpr := [|0, 0, 0, 1|]+def dt : DiffForm MathValue := [|1, 0, 0, 0|]+def dx : DiffForm MathValue := [|0, 1, 0, 0|]+def dy : DiffForm MathValue := [|0, 0, 1, 0|]+def dz : DiffForm MathValue := [|0, 0, 0, 1|]  assertEqual "Hodge star of dt ∧ dx"   (hodge (wedge dt dx))
sample/math/geometry/hodge-laplacian-polar.egi view
@@ -1,35 +1,38 @@-declare symbol r, θ: MathExpr+declare symbol r, θ: MathValue  -- Parameters and metrics  def N : Integer := 2 -def x : Vector MathExpr := [|r, θ|]+def x : Vector MathValue := [|r, θ|] -def g_i_j : Matrix MathExpr := [| [| 1, 0 |], [| 0, r^2 |] |]_i_j-def g~i~j : Matrix MathExpr := [| [| 1, 0 |], [| 0, 1 / r^2 |] |]~i~j+def g_i_j : Matrix MathValue := [| [| 1, 0 |], [| 0, r^2 |] |]_i_j+def g~i~j : Matrix MathValue := [| [| 1, 0 |], [| 0, 1 / r^2 |] |]~i~j  -- Hodge Laplacian -def d (A: Tensor MathExpr) : Tensor MathExpr := !(flip ∂/∂) x A+def d (A: Tensor MathValue) : Tensor MathValue := !(flip ∂/∂) x A -def hodge (A: Tensor MathExpr) : Tensor MathExpr :=-  let k := dfOrder A in-    withSymbols [i, j]-      (sqrt (M.det g_#_#)) * (foldl (.) ((subrefs A (map 1#j_$1 (between 1 k))) . (subrefs (ε' N k) (map 1#i_$1 (between 1 N))))-                                        (map 1#g~(i_$1)~(j_$1) [1..k]))+def hodge (A: Tensor MathValue) : Tensor MathValue :=+  let k := dfOrder A+   in withSymbols [i, j]+        sqrt (abs (M.det g_#_#)) *+        foldl+          (.)+          ((ε' N k)_(i_1)..._(i_N) . A..._(j_1)..._(j_k))+          (map (\n -> g~(i_n)~(j_n)) [1..k])  -def δ (A: Tensor MathExpr) : Tensor MathExpr :=+def δ (A: Tensor MathValue) : Tensor MathValue :=   let k := dfOrder A in     -1^(N * (k + 1) + 1) * (hodge (d (hodge A))) -def Δ (A: Tensor MathExpr) : Tensor MathExpr :=+def Δ (A: Tensor MathValue) : Tensor MathValue :=   match (dfOrder A) as integer with   | #0 -> δ (d A)   | #N -> d (δ A)   | _  -> d (δ A) + δ (d A) -def f : MathExpr := function (r, θ)+def f : MathValue := function (r, θ)  assertEqual "Laplacian" (Δ f) ((-1 / r^2) * ((∂/∂ (∂/∂ f θ) θ) + r * (∂/∂ f r) + (r^2 * (∂/∂ (∂/∂ f r) r))))
sample/math/geometry/hodge-laplacian-spherical.egi view
@@ -4,31 +4,31 @@  def N : Integer := 3 -def x : Vector MathExpr := [| r, θ, φ |]+def x : Vector MathValue := [| r, θ, φ |] -def g_i_j : Matrix MathExpr := [| [| 1, 0, 0 |], [| 0, r^2, 0 |], [| 0, 0, r^2 * (sin θ)^2 |] |]_i_j-def g~i~j : Matrix MathExpr := [| [| 1, 0, 0 |], [| 0, 1 / r^2, 0 |], [| 0, 0, 1 / (r^2 * (sin θ)^2) |] |]~i~j+def g_i_j : Matrix MathValue := [| [| 1, 0, 0 |], [| 0, r^2, 0 |], [| 0, 0, r^2 * (sin θ)^2 |] |]_i_j+def g~i~j : Matrix MathValue := [| [| 1, 0, 0 |], [| 0, 1 / r^2, 0 |], [| 0, 0, 1 / (r^2 * (sin θ)^2) |] |]~i~j  -- Exterior derivative-def d (A: Tensor MathExpr) : Tensor MathExpr := !(flip ∂/∂) x A+def d (A: Tensor MathValue) : Tensor MathValue := !(flip ∂/∂) x A  -- Hodge star operator-def hodge (A: DiffForm MathExpr) : DiffForm MathExpr :=+def hodge (A: DiffForm MathValue) : DiffForm MathValue :=   let k := dfOrder A    in withSymbols [i, j]         sqrt (abs (M.det g_#_#)) *         foldl           (.)-          ((subrefs A (map 1#j_$1 (between 1 k))) . (subrefs (ε' N k) (map 1#i_$1 (between 1 N))))-          (map (\n -> g~(i_n)~(j_n)) (between 1 k))+          ((ε' N k)_(i_1)..._(i_N) . A..._(j_1)..._(j_k))+          (map (\n -> g~(i_n)~(j_n)) [1..k])  -- Codifferential-def δ (A: DiffForm MathExpr) : DiffForm MathExpr :=+def δ (A: DiffForm MathValue) : DiffForm MathValue :=   let k := dfOrder A    in ((-1)^(N * k + 1)) * hodge (d (hodge A))  -- Laplacian-def Δ (A: DiffForm MathExpr) : DiffForm MathExpr :=+def Δ (A: DiffForm MathValue) : DiffForm MathValue :=   match dfOrder A as integer with     | #0 -> δ (d A)     | #N -> d (δ A)
+ sample/math/geometry/kahler-geometry-of-CP1.egi view
@@ -0,0 +1,85 @@+--+-- Kaehler geometry of CP1 in Wirtinger calculus (Fubini-Study metric)+--+-- Wirtinger calculus treats z and zbar as independent symbols; the complex+-- structure enters through the library symbol i (i^2 = -1, a symbol-carried+-- quotient). This sample showcases the extensible CAS tower+-- (design/type-cas-tower.md):+--   * cas-type aliases for complex-coefficient rings (Phase alpha)+--   * annotation-selected canonical forms: the same value viewed flat+--     (Z[i, z, zbar]) or nested (coefficients in Z[i], organized by z and+--     zbar), with the reshape absorption law making round trips safe+--     (Phase gamma-prime)+--++declare symbol z+declare symbol zbar+declare symbol ztmp   -- scratch symbol for the z <-> zbar swap in conjC++declare cas-type GaussianInt := Poly Integer [i]+declare cas-type CPoly := Poly (Poly Integer [i]) [z, zbar]   -- Z[i][z, zbar]++--+-- 1. Complex-coefficient polynomials: one value, two canonical forms+--++def gaussNorm : GaussianInt := (2 + 3 * i) * (2 - 3 * i)+assertEqual "norm in Z[i]" gaussNorm 13++def f := (1 + i) * z^2 + (2 - i) * z * zbar + 3 * i++def fNested : CPoly := f                       -- coefficients collected in Z[i]+def fFlat : Poly Integer [i, z, zbar] := f     -- fully flat form++assertEqual "the annotation selects the nested canonical form"+  (typeOf fNested) "Poly (Poly Integer [i]) [z, zbar]"+assertEqual "flat and nested forms agree semantically"+  ((fNested - fFlat) = 0) True+assertEqual "absorption law: nested -> flat = direct flat"+  (show (fNested : Poly Integer [i, z, zbar])) (show fFlat)++-- anti-holomorphic conjugation as a substitution. `substitute` applies its+-- pairs sequentially, so the z <-> zbar swap goes through a scratch symbol+def conjC (v : MathValue) : MathValue :=+  substitute [(i, - i), (z, ztmp), (zbar, z), (ztmp, zbar)] v++assertEqual "|f|^2 is real (fixed by conjugation)"+  ((conjC (f * conjC f) - f * conjC f) = 0) True++-- Cauchy-Riemann: holomorphic expressions are annihilated by d/dzbar+assertEqual "d/dzbar kills holomorphic polynomials"+  (∂/∂ (z^3 + (1 + i) * z + 2) zbar) 0++--+-- 2. Fubini-Study metric from the Kaehler potential+--    K = log(1 + z zbar),  g = d2 K / dz dzbar = 1 / (1 + z zbar)^2+--++def K := log (1 + z * zbar)+def g := ∂/∂ (∂/∂ K z) zbar++assertEqual "g = 1/(1 + z zbar)^2"+  ((g - 1 / (1 + z * zbar)^2) = 0) True+assertEqual "g is real" ((conjC g - g) = 0) True++--+-- 3. Ricci form and the Kaehler-Einstein property+--    Ric = - d2 (log g) / dz dzbar = 2 g+--    (Einstein constant 2: constant positive curvature, the round sphere)+--++def ricci := 0 - ∂/∂ (∂/∂ (log g) z) zbar++assertEqual "Kaehler-Einstein: Ric = 2 g"+  ((ricci - 2 * g) = 0) True++--+-- 4. A Laplace eigenfunction on the sphere+--    u = (1 - z zbar)/(1 + z zbar) is the first spherical harmonic:+--    Delta u = (1/g) d2u/dz dzbar = -2 u+--++def u := (1 - z * zbar) / (1 + z * zbar)++assertEqual "first eigenfunction: d2u/dzdzbar = -2 u g"+  ((∂/∂ (∂/∂ u z) zbar - (-2) * u * g) = 0) True
sample/math/geometry/polar-laplacian-2d-2.egi view
@@ -1,33 +1,42 @@-declare symbol r, θ : MathExpr+declare symbol r, θ : MathValue -- Polar Laplacian in 2D using tensor notation -def x : Vector MathExpr := [| r, θ |]+def x : Vector MathValue := [| r, θ |] -def X : Vector MathExpr := [| r * cos θ, r * sin θ |]+def X : Vector MathValue := [| r * cos θ, r * sin θ |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensor-def g_i_j : Matrix MathExpr := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [2, 2]-def g~i~j : Matrix MathExpr := M.inverse g_#_#+def g_i_j : Matrix MathValue := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [2, 2]+def g~i~j : Matrix MathValue := M.inverse g_#_# -g_#_#+assertEqual "polar metric g_i_j = diag(1, r^2)"+  g_#_#+  [| [| 1, 0 |], [| 0, r^2 |] |] -g~#~#+assertEqual "inverse polar metric g~i~j = diag(1, 1/r^2)"+  g~#~#+  [| [| 1, 0 |], [| 0, 1 / r^2 |] |]  -- Christoffel symbols-def Γ_i_j_k : Tensor MathExpr := withSymbols [j, k, l]+def Γ_i_j_k : Tensor MathValue := withSymbols [j, k, l]   (1 / 2) * (∂/∂ g_j_l x~k + ∂/∂ g_j_k x~l - ∂/∂ g_k_l x~j) -def Γ~i_j_k : Tensor MathExpr := withSymbols [i, j, k, l]+def Γ~i_j_k : Tensor MathValue := withSymbols [i, j, k, l]   g~i~j . Γ_j_k_l -def f : MathExpr := function (r, θ)+def f : MathValue := function (r, θ)  -- Laplacian-def Laplacian : MathExpr := withSymbols [i, j, k]+def Laplacian : MathValue := withSymbols [i, j, k]   g~i~j . ∂/∂ (∂/∂ f x~j) x~i - g~i~j . Γ~k_i_j . ∂/∂ f x~k -Laplacian+-- Standard polar Laplacian: ∂²f/∂r² + (1/r) ∂f/∂r + (1/r²) ∂²f/∂θ²+assertEqual "polar Laplacian"+  Laplacian+  ((userRefs f [1, 1]) r θ+   + (userRefs f [1]) r θ / r+   + (userRefs f [2, 2]) r θ / r^2) 
sample/math/geometry/polar-laplacian-2d-3.egi view
@@ -1,33 +1,33 @@ -- Polar Laplacian in 2D using function symbol -declare symbol r, θ : MathExpr+declare symbol r, θ : MathValue  def f := function (r, θ) -def x : Vector MathExpr := [| r, θ |]+def x : Vector MathValue := [| r, θ |] -def X : Vector MathExpr := [| r * cos θ, r * sin θ |]+def X : Vector MathValue := [| r * cos θ, r * sin θ |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensor-def g_i_j : Matrix MathExpr := generateTensor (\[a, b] -> V.* e_a e_b) [2, 2]-def g~i~j : Matrix MathExpr := M.inverse g_#_#+def g_i_j : Matrix MathValue := generateTensor (\[a, b] -> V.* e_a e_b) [2, 2]+def g~i~j : Matrix MathValue := M.inverse g_#_#  assertEqual "Metric tensor"   g_#_#   [| [| 1, 0 |], [| 0, r^2 |] |]_#_#  -- Christoffel symbols-def Γ_i_j_k : Tensor MathExpr := withSymbols [j, k, l]+def Γ_i_j_k : Tensor MathValue := withSymbols [j, k, l]   (1 / 2) * (∂/∂ g_j_l x~k + ∂/∂ g_j_k x~l - ∂/∂ g_k_l x~j) -def Γ~i_j_k : Tensor MathExpr := withSymbols [i, j, k, l]+def Γ~i_j_k : Tensor MathValue := withSymbols [i, j, k, l]   g~i~j . Γ_j_k_l  -- Laplacian via Christoffel symbols-def Laplacian : MathExpr := withSymbols [i, j, k]+def Laplacian : MathValue := withSymbols [i, j, k]   g~i~j . ∂/∂ (∂/∂ f x~j) x~i - g~i~j . Γ~k_i_j . ∂/∂ f x~k  assertEqual "Laplacian in polar coordinates"
sample/math/geometry/polar-laplacian-2d.egi view
@@ -2,24 +2,30 @@ -- 2D Polar Laplacian using chain rule -- -declare symbol r, θ : MathExpr+declare symbol r, θ : MathValue -def x : MathExpr := r * cos θ-def y : MathExpr := r * sin θ+def x : MathValue := r * cos θ+def y : MathValue := r * sin θ  def u := function (x, y) -def uR : MathExpr := ∂/∂ u r+def uR : MathValue := ∂/∂ u r -assert "∂u/∂r"-  (show uR = "u|1 (r * 'cos θ) (r * 'sin θ) * 'cos θ + u|2 (r * 'cos θ) (r * 'sin θ) * 'sin θ")+-- Chain rule: ∂u/∂r = u|1 * ∂x/∂r + u|2 * ∂y/∂r = u|1 * cos θ + u|2 * sin θ.+-- u|i (the i-th partial of u) is constructed via `userRefs u [i]`.+assertEqual "∂u/∂r"+  uR+  (cos θ * (userRefs u [1]) (r * cos θ) (r * sin θ)+   + sin θ * (userRefs u [2]) (r * cos θ) (r * sin θ)) -def uRR : MathExpr := ∂/∂ (∂/∂ u r) r+def uRR : MathValue := ∂/∂ (∂/∂ u r) r -def uΘ : MathExpr := ∂/∂ u θ-def uΘΘ : MathExpr := ∂/∂ (∂/∂ u θ) θ+def uΘ : MathValue := ∂/∂ u θ+def uΘΘ : MathValue := ∂/∂ (∂/∂ u θ) θ  -- Laplacian in polar coordinates: ∂²u/∂r² + (1/r)∂u/∂r + (1/r²)∂²u/∂θ²--- Full Laplacian should simplify to u|1|1 + u|2|2-assert "Full Laplacian in polar coordinates"-  (show (uRR + 1 / r * uR + 1 / r ^ 2 * uΘΘ) = "u|2|2 (r * 'cos θ) (r * 'sin θ) + u|1|1 (r * 'cos θ) (r * 'sin θ)")+-- Should simplify to u|1|1 + u|2|2 (the cartesian Laplacian).+assertEqual "Full Laplacian in polar coordinates"+  (uRR + 1 / r * uR + 1 / r ^ 2 * uΘΘ)+  ((userRefs u [1, 1]) (r * cos θ) (r * sin θ)+   + (userRefs u [2, 2]) (r * cos θ) (r * sin θ))
sample/math/geometry/polar-laplacian-3d-2.egi view
@@ -1,6 +1,6 @@ -- Spherical Laplacian in 3D using tensor notation -declare symbol r, θ, φ : MathExpr+declare symbol r, θ, φ : MathValue  def f := function (r, θ, φ) @@ -9,7 +9,7 @@ def X := [| r * sin θ * cos φ, r * sin θ * sin φ, r * cos θ |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensor def g_i_j := generateTensor (\[a, b] -> V.* e_a e_b) [3, 3]
sample/math/geometry/polar-laplacian-3d-3.egi view
@@ -1,6 +1,6 @@ -- Spherical Laplacian in 3D using function symbol -declare symbol r, θ, φ : MathExpr+declare symbol r, θ, φ : MathValue  def f := function (r, θ, φ) @@ -9,7 +9,7 @@ def X := [| r * sin θ * cos φ, r * sin θ * sin φ, r * cos θ |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensor def g_i_j := generateTensor (\[a, b] -> V.* e_a e_b) [3, 3]
sample/math/geometry/polar-laplacian-3d.egi view
@@ -2,7 +2,7 @@ -- 3D Polar (Spherical) Laplacian using chain rule -- -declare symbol r, θ, φ : MathExpr+declare symbol r, θ, φ : MathValue  def x := r * sin θ * cos φ def y := r * sin θ * sin φ@@ -19,6 +19,9 @@  -- Laplacian in spherical coordinates: -- Δu = ∂²u/∂r² + (2/r)∂u/∂r + (1/r²)∂²u/∂θ² + (cos θ / (r² sin θ))∂u/∂θ + (1/(r sin θ)²)∂²u/∂φ²--- Should simplify to u|1|1 + u|2|2 + u|3|3-assert "Laplacian in spherical coordinates"-  (show (uRR + 2 / r * uR + 1 / r ^ 2 * uΘΘ + cos θ / (r ^ 2 * sin θ) * uΘ + 1 / (r * sin θ) ^ 2 * uΦΦ) = "u|2|2 (r * 'sin θ * 'cos φ) (r * 'sin θ * 'sin φ) (r * 'cos θ) + u|1|1 (r * 'sin θ * 'cos φ) (r * 'sin θ * 'sin φ) (r * 'cos θ) + u|3|3 (r * 'sin θ * 'cos φ) (r * 'sin θ * 'sin φ) (r * 'cos θ)")+-- Should simplify to u|1|1 + u|2|2 + u|3|3 (the cartesian Laplacian).+assertEqual "Laplacian in spherical coordinates"+  (uRR + 2 / r * uR + 1 / r ^ 2 * uΘΘ + cos θ / (r ^ 2 * sin θ) * uΘ + 1 / (r * sin θ) ^ 2 * uΦΦ)+  ((userRefs u [1, 1]) (r * sin θ * cos φ) (r * sin θ * sin φ) (r * cos θ)+   + (userRefs u [2, 2]) (r * sin θ * cos φ) (r * sin θ * sin φ) (r * cos θ)+   + (userRefs u [3, 3]) (r * sin θ * cos φ) (r * sin θ * sin φ) (r * cos θ))
sample/math/geometry/riemann-curvature-tensor-of-FLRW-metric.egi view
@@ -1,16 +1,22 @@-declare symbol w, r, θ, φ, K: MathExpr+declare symbol w, r, θ, φ, K: MathValue  -- Parameters-def x : Vector MathExpr := [| w, r, θ, φ |]+def x : Vector MathValue := [| w, r, θ, φ |]  -- Scale factor function a(w) def a := function (w) --- Spatial curvature factor-def W (r: MathExpr) : MathExpr := 1 / '(1 - K * r^2)+assertEqual "x" x [| w, r, θ, φ |] +-- Spatial curvature factor.+def W (r: MathValue) : MathValue := 1 / `(1 - K * r^2)++assertEqual "W r = 1/(1-Kr^2)"+  (W r)+  (1 / `(1 - K * r^2))+ -- Metric tensor-def g_i_j : Matrix MathExpr :=+def g_i_j : Matrix MathValue :=   [| [| -1, 0, 0, 0 |]    , [| 0, a^2 * W r, 0, 0 |]    , [| 0, 0, a^2 * r^2, 0 |]@@ -37,5 +43,9 @@ def scalarCurvature := withSymbols [i, j]   expandAll' (g~i~j . Ric_i_j) --- Note: The expected scalar curvature is:--- (6 * a|1|1 * a + 6 * (a|1)^2 + 6 * K) / a^2+-- Expected scalar curvature of the FLRW metric (in units c=1):+--   R = 6 (a''(w) a + (a'(w))^2 + K) / a^2+-- The full computation is expensive (>5min on this machine); evaluating+-- `scalarCurvature` is left as the file's final value, not asserted, to+-- keep `cabal test` runtime reasonable. To verify the expected form+-- explicitly, run: `cabal run egison -- -t <this-file>` and compare.
sample/math/geometry/riemann-curvature-tensor-of-S2.egi view
@@ -1,28 +1,28 @@-declare symbol r, θ, φ: MathExpr+declare symbol r, θ, φ: MathValue  -- Parameters-def x : Vector MathExpr := [| θ, φ |]+def x : Vector MathValue := [| θ, φ |] -def X : Vector MathExpr := [| r * sin θ * cos φ -- x+def X : Vector MathValue := [| r * sin θ * cos φ -- x           , r * sin θ * sin φ -- y           , r * cos θ         -- z           |] -def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensors-def g[_i_j] : Matrix MathExpr := generateTensor (\[a, b] -> V.* e_a e_b) [2, 2]-def g[~i~j] : Matrix MathExpr := M.inverse g_#_#+def g[_i_j] : Matrix MathValue := generateTensor (\[a, b] -> V.* e_a e_b) [2, 2]+def g[~i~j] : Matrix MathValue := M.inverse g_#_#  assertEqual "Metric tensor"   g_#_#   [| [| r^2, 0 |], [| 0, r^2 * (sin θ)^2 |] |]_#_# assertEqual "Metric tensor"   g~#~#-  [| [| 1 / r^2, 0 |], [| 0, 1 / (r^2 * (sin θ)^2) |] |]~#~#+  [| [| r^(-2), 0 |], [| 0, r^(-2) * (sin θ)^(-2) |] |]~#~#  -- Christoffel symbols-def Γ_i[_j_k] : Tensor MathExpr := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i)+def Γ_i[_j_k] : Tensor MathValue := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i)  assertEqual "Christoffel symbols of the first kind"   Γ_1_#_#@@ -31,7 +31,7 @@   Γ_2_#_#   [| [| 0, r^2 * (sin θ) * (cos θ) |], [| r^2 * (sin θ) * (cos θ), 0 |] |]_#_# -def Γ~i_j_k : Tensor MathExpr := withSymbols [m]+def Γ~i_j_k : Tensor MathValue := withSymbols [m]   g~i~m . Γ_m_j_k  assertEqual "Christoffel symbols of the second kind"@@ -42,7 +42,7 @@   [| [| 0, (cos θ) / (sin θ) |], [| (cos θ) / (sin θ), 0 |] |]_#_#  -- Riemann curvature-def R~i_j_k_l : Tensor MathExpr := withSymbols [m]+def R~i_j_k_l : Tensor MathValue := withSymbols [m]   ∂/∂ Γ~i_j_l x~k - ∂/∂ Γ~i_j_k x~l + Γ~m_j_l . Γ~i_m_k - Γ~m_j_k . Γ~i_m_l  assertEqual "riemann curvature"@@ -59,13 +59,13 @@   [| [| 0, 0 |], [| 0, 0 |] |]~#_#  -- Ricci curvature-def Ric[_i_j] : Matrix MathExpr := withSymbols [m]+def Ric[_i_j] : Matrix MathValue := withSymbols [m]   sum (contract R~m_i_m_j)  -- Scalar curvature-def scalarCurvature : MathExpr := withSymbols [i, j]+def scalarCurvature : MathValue := withSymbols [i, j]   g~i~j . Ric_i_j  assertEqual "scalar curvature"   scalarCurvature-  (2 / r^2)+  (2 * r^(-2))
sample/math/geometry/riemann-curvature-tensor-of-S2xS3.egi view
@@ -5,33 +5,33 @@ def x := [| φ, θ, ψ, y, α |]  def g_i_j :=-  [| [| (3 * '(1 + (- y))^2 * (sin θ)^2 * '(a + (- (y^2))) +-         2 * '(a + (-3) * y^2 + 2 * y^3) * (cos θ)^2 * '(1 + (- y)) +-         '(a + (-2) * y + y^2)^2 * (cos θ)^2) /-        (18 * '(a + (- (y^2))) * '(1 + (- y)))+  [| [| (3 * `(1 + (- y))^2 * (sin θ)^2 * `(a + (- (y^2))) ++         2 * `(a + (-3) * y^2 + 2 * y^3) * (cos θ)^2 * `(1 + (- y)) ++         `(a + (-2) * y + y^2)^2 * (cos θ)^2) /+        (18 * `(a + (- (y^2))) * `(1 + (- y)))       , 0-      , ((-2) * '(a + (-3) * y^2 + 2 * y^3) * cos θ * '(1 + (- y)) +-         (- ('(a + (-2) * y + y^2)^2)) * cos θ) /-        (18 * '(a + (- (y^2))) * '(1 + (- y)))+      , ((-2) * `(a + (-3) * y^2 + 2 * y^3) * cos θ * `(1 + (- y)) ++         (- (`(a + (-2) * y + y^2)^2)) * cos θ) /+        (18 * `(a + (- (y^2))) * `(1 + (- y)))       , 0-      , (- '(a + (-2) * y + y^2)) * cos θ / (3 * '(1 + (- y)))+      , (- `(a + (-2) * y + y^2)) * cos θ / (3 * `(1 + (- y)))       |]-   , [| 0, '(1 + (- y)) / 6, 0, 0, 0 |]-   , [| ((-2) * '(a + (-3) * y^2 + 2 * y^3) * cos θ * '(1 + (- y)) +-         (- ('(a + (-2) * y + y^2)^2)) * cos θ) /-        (18 * '(a + (- (y^2))) * '(1 + (- y)))+   , [| 0, `(1 + (- y)) / 6, 0, 0, 0 |]+   , [| ((-2) * `(a + (-3) * y^2 + 2 * y^3) * cos θ * `(1 + (- y)) ++         (- (`(a + (-2) * y + y^2)^2)) * cos θ) /+        (18 * `(a + (- (y^2))) * `(1 + (- y)))       , 0-      , (2 * '(a + (-3) * y^2 + 2 * y^3) * '(1 + (- y)) +-         '(a + (-2) * y + y^2)^2) / (18 * '(a + (- (y^2))) * '(1 + (- y)))+      , (2 * `(a + (-3) * y^2 + 2 * y^3) * `(1 + (- y)) ++         `(a + (-2) * y + y^2)^2) / (18 * `(a + (- (y^2))) * `(1 + (- y)))       , 0-      , 1 * '(a + (-2) * y + y^2) / (3 * '(1 + (- y)))+      , 1 * `(a + (-2) * y + y^2) / (3 * `(1 + (- y)))       |]-   , [| 0, 0, 0, '(1 + (- y)) / (2 * '(a + (-3) * y^2 + 2 * y^3)), 0 |]-   , [| (- '(a + (-2) * y + y^2)) * cos θ / (3 * '(1 + (- y)))+   , [| 0, 0, 0, `(1 + (- y)) / (2 * `(a + (-3) * y^2 + 2 * y^3)), 0 |]+   , [| (- `(a + (-2) * y + y^2)) * cos θ / (3 * `(1 + (- y)))       , 0-      , 1 * '(a + (-2) * y + y^2) / (3 * '(1 + (- y)))+      , 1 * `(a + (-2) * y + y^2) / (3 * `(1 + (- y)))       , 0-      , 2 * '(a + (- (y^2))) / '(1 + (- y))+      , 2 * `(a + (- (y^2))) / `(1 + (- y))       |]    |]_#_# 
sample/math/geometry/riemann-curvature-tensor-of-S3.egi view
@@ -1,30 +1,30 @@-declare symbol r, θ, φ, ψ: MathExpr+declare symbol r, θ, φ, ψ: MathValue  -- Parameters-def x : Vector MathExpr := [| θ, φ, ψ |]+def x : Vector MathValue := [| θ, φ, ψ |] -def X : Vector MathExpr := [| r * cos θ+def X : Vector MathValue := [| r * cos θ           , r * sin θ * cos φ           , r * sin θ * sin φ * cos ψ           , r * sin θ * sin φ * sin ψ           |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensors---def g_i_j : Matrix MathExpr := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [3, 3]-def g_i_j : Matrix MathExpr := generateTensor (\[a, b] -> V.* e_a e_b) [3, 3]-def g~i~j : Matrix MathExpr := M.inverse g_#_#+--def g_i_j : Matrix MathValue := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [3, 3]+def g_i_j : Matrix MathValue := generateTensor (\[a, b] -> V.* e_a e_b) [3, 3]+def g~i~j : Matrix MathValue := M.inverse g_#_#  assertEqual "Metric tensor g_#_#"   g_#_#   [| [| r^2, 0, 0 |], [| 0, r^2 * (sin θ)^2, 0 |], [| 0, 0, r^2 * (sin θ)^2 * (sin φ)^2 |] |]_#_#  -- Christoffel symbols-def Γ_i_j_k : Tensor MathExpr := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i)+def Γ_i_j_k : Tensor MathValue := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i) -def Γ~i_j_k : Tensor MathExpr := withSymbols [m]+def Γ~i_j_k : Tensor MathValue := withSymbols [m]   g~i~m . Γ_m_j_k  assertEqual "Christoffel symbols of the second kind Γ~1_#_#"@@ -39,7 +39,7 @@   -- Riemann curvature-def R~i_j_k_l : Tensor MathExpr := withSymbols [m]+def R~i_j_k_l : Tensor MathValue := withSymbols [m]   ∂/∂ Γ~i_j_l x~k - ∂/∂ Γ~i_j_k x~l + Γ~m_j_l . Γ~i_m_k - Γ~m_j_k . Γ~i_m_l  assertEqual "Riemann curvature R~#_#_1_1"@@ -71,7 +71,7 @@   [| [| 0, 0, 0 |], [| 0, 0, 0 |], [| 0, 0, 0 |] |]~#_#  -- Ricci curvature-def Ric_i_j : Tensor MathExpr := withSymbols [m]+def Ric_i_j : Tensor MathValue := withSymbols [m]   sum (contract R~m_i_m_j)  assertEqual "Ricci curvature Ric_#_#"@@ -79,7 +79,7 @@   [| [| 2, 0, 0 |], [| 0, 2 * (sin θ)^2, 0 |], [| 0, 0, 2 * (sin θ)^2 * (sin φ)^2 |] |]_#_#  -- Scalar curvature-def scalarCurvature : MathExpr := withSymbols [i, j]+def scalarCurvature : MathValue := withSymbols [i, j]   g~i~j . Ric_i_j  assertEqual "scalar curvature"
sample/math/geometry/riemann-curvature-tensor-of-S4.egi view
@@ -1,7 +1,7 @@ -- Parameters-def x : Vector MathExpr := [| θ, φ, ψ, η |]+def x : Vector MathValue := [| θ, φ, ψ, η |] -def X : Vector MathExpr := [| r * cos θ+def X : Vector MathValue := [| r * cos θ           , r * sin θ * cos φ           , r * sin θ * sin φ * cos ψ           , r * sin θ * sin φ * sin ψ * cos η@@ -9,11 +9,11 @@           |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensors-def g_i_j : Matrix MathExpr := generateTensor (\[a, b] -> V.* e_a e_b) [4, 4]-def g~i~j : Matrix MathExpr := M.inverse g_#_#+def g_i_j : Matrix MathValue := generateTensor (\[a, b] -> V.* e_a e_b) [4, 4]+def g~i~j : Matrix MathValue := M.inverse g_#_#  assertEqual "Metric tensor g_1_#"   g_1_#
sample/math/geometry/riemann-curvature-tensor-of-S5-non-sym.egi view
@@ -1,4 +1,6 @@ -- Parameters+declare symbol r, θ, φ, ψ, η, δ : MathValue+ def x := [|θ, φ, ψ, η, δ|]  def X := [| r * (cos θ),
sample/math/geometry/riemann-curvature-tensor-of-S5.egi view
@@ -1,4 +1,6 @@ -- Parameters+declare symbol r, θ, φ, ψ, η, δ : MathValue+ def x := [| θ, φ, ψ, η, δ |]  def X := [| r * cos θ@@ -10,7 +12,7 @@           |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensors def g_i_j := generateTensor (\[a, b] -> V.* e_a e_b) [5, 5]
sample/math/geometry/riemann-curvature-tensor-of-S7.egi view
@@ -1,19 +1,21 @@ -- Riemann curvature tensor of S7 -def x := [| α, β, γ, δ, ε, ζ, η |]+declare symbol α, β, γ, δ, ξ, ζ, η, r : MathValue +def x := [| α, β, γ, δ, ξ, ζ, η |]+ def X := [| r * cos α           , r * sin α * cos β           , r * sin α * sin β * cos γ           , r * sin α * sin β * sin γ * cos δ-          , r * sin α * sin β * sin γ * sin δ * cos ε-          , r * sin α * sin β * sin γ * sin δ * sin ε * cos ζ-          , r * sin α * sin β * sin γ * sin δ * sin ε * sin ζ * cos η-          , r * sin α * sin β * sin γ * sin δ * sin ε * sin ζ * sin η+          , r * sin α * sin β * sin γ * sin δ * cos ξ+          , r * sin α * sin β * sin γ * sin δ * sin ξ * cos ζ+          , r * sin α * sin β * sin γ * sin δ * sin ξ * sin ζ * cos η+          , r * sin α * sin β * sin γ * sin δ * sin ξ * sin ζ * sin η           |]  -- Local basis-def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensor def g_i_j := generateTensor (\[a, b] -> V.* e_a e_b) [7, 7]@@ -39,8 +41,8 @@    , [| 0, 0, -1 * sin α * cos α * (sin β)^2, 0, 0, 0, 0 |]    , [| 0, 0, 0, -1 * sin α * cos α * (sin β)^2 * (sin γ)^2, 0, 0, 0 |]    , [| 0, 0, 0, 0, -1 * sin α * cos α * (sin β)^2 * (sin γ)^2 * (sin δ)^2, 0, 0 |]-   , [| 0, 0, 0, 0, 0, -1 * sin α * cos α * (sin β)^2 * (sin γ)^2 * (sin δ)^2 * (sin ε)^2, 0 |]-   , [| 0, 0, 0, 0, 0, 0, -1 * sin α * cos α * (sin β)^2 * (sin γ)^2 * (sin δ)^2 * (sin ε)^2 * (sin ζ)^2 |]+   , [| 0, 0, 0, 0, 0, -1 * sin α * cos α * (sin β)^2 * (sin γ)^2 * (sin δ)^2 * (sin ξ)^2, 0 |]+   , [| 0, 0, 0, 0, 0, 0, -1 * sin α * cos α * (sin β)^2 * (sin γ)^2 * (sin δ)^2 * (sin ξ)^2 * (sin ζ)^2 |]    |]_#_# assertEqual "Christoffel symbols of the second kind Γ~2_#_#"   Γ~2_#_#@@ -49,8 +51,8 @@    , [| 0, 0, -1 * sin β * cos β, 0, 0, 0, 0 |]    , [| 0, 0, 0, -1 * sin β * cos β * (sin γ)^2, 0, 0, 0 |]    , [| 0, 0, 0, 0, -1 * sin β * cos β * (sin γ)^2 * (sin δ)^2, 0, 0 |]-   , [| 0, 0, 0, 0, 0, -1 * sin β * cos β * (sin γ)^2 * (sin δ)^2 * (sin ε)^2, 0 |]-   , [| 0, 0, 0, 0, 0, 0, -1 * sin β * cos β * (sin γ)^2 * (sin δ)^2 * (sin ε)^2 * (sin ζ)^2 |]+   , [| 0, 0, 0, 0, 0, -1 * sin β * cos β * (sin γ)^2 * (sin δ)^2 * (sin ξ)^2, 0 |]+   , [| 0, 0, 0, 0, 0, 0, -1 * sin β * cos β * (sin γ)^2 * (sin δ)^2 * (sin ξ)^2 * (sin ζ)^2 |]    |]_#_#  -- Riemann curvature
sample/math/geometry/riemann-curvature-tensor-of-Schwarzschild-metric.egi view
@@ -6,8 +6,8 @@  -- Schwarzschild metric def g_i_j :=-  [| [| '(c^2 * r - 2 * G * M) / (c^2 * r), 0, 0, 0 |]-   , [| 0, (-1) / ('(c^2 * r - 2 * G * M) / (c^2 * r)), 0, 0 |]+  [| [| `(c^2 * r - 2 * G * M) / (c^2 * r), 0, 0, 0 |]+   , [| 0, (-1) / (`(c^2 * r - 2 * G * M) / (c^2 * r)), 0, 0 |]    , [| 0, 0, -r^2, 0 |]    , [| 0, 0, 0, -r^2 * (sin θ)^2 |]    |]@@ -16,10 +16,10 @@  assertEqual "Metric tensor g_1_#"   g_1_#-  [| '(c^2 * r - 2 * G * M) / (c^2 * r), 0, 0, 0 |]_#+  [| `(c^2 * r - 2 * G * M) / (c^2 * r), 0, 0, 0 |]_# assertEqual "Metric tensor g_2_#"   g_2_#-  [| 0, (-1) / ('(c^2 * r - 2 * G * M) / (c^2 * r)), 0, 0 |]_#+  [| 0, (-1) / (`(c^2 * r - 2 * G * M) / (c^2 * r)), 0, 0 |]_# assertEqual "Metric tensor g_3_#"   g_3_#   [| 0, 0, -r^2, 0 |]_#@@ -33,19 +33,25 @@ def Γ~i_j_k := withSymbols [m]   g~i~m . Γ_m_j_k +-- The Christoffel symbols Γ~1 and Γ~2 are mathematically equal to+--   GM / (c²r² - 2GMr)  and  GM(c²r - 2GM)/(c⁴r⁴), etc.+-- but the current CAS does not combine these into a single fraction+-- (it leaves them as `(c²r - 2GM)` opaque atom plus `r⁻¹`/`c²` factors).+-- The forms below match what the CAS actually produces. assertEqual "Christoffel symbols of the second kind Γ~1_#_#"   Γ~1_#_#-  [| [| 0, G * M / (c^2 * r^2 - 2 * G * M * r), 0, 0 |]-   , [| G * M / (c^2 * r^2 - 2 * G * M * r), 0, 0, 0 |]+  [| [| 0, (1 / 2) * `(c^2 * r - 2 * G * M)^(-1) * c^2 - (1 / 2) / r, 0, 0 |]+   , [| (1 / 2) * `(c^2 * r - 2 * G * M)^(-1) * c^2 - (1 / 2) / r, 0, 0, 0 |]    , [| 0, 0, 0, 0 |]    , [| 0, 0, 0, 0 |]    |]_#_# assertEqual "Christoffel symbols of the second kind Γ~2_#_#"   Γ~2_#_#-  [| [| G * M * (c^2 * r - 2 * G * M) / (c^4 * r^4), 0, 0, 0 |]-   , [| 0, -1 * G * M / (c^2 * r^2 - 2 * G * M * r), 0, 0 |]-   , [| 0, 0, -1 * r + 2 * G * M / c^2, 0 |]-   , [| 0, 0, 0, (-1 * r + 2 * G * M / c^2) * (sin θ)^2 |]+  [| [| (1 / 2) * `(c^2 * r - 2 * G * M) / (c^2 * r^2)+        - (1 / 2) * `(c^2 * r - 2 * G * M)^2 / (c^4 * r^3), 0, 0, 0 |]+   , [| 0, - (1 / 2) * `(c^2 * r - 2 * G * M)^(-1) * c^2 + (1 / 2) / r, 0, 0 |]+   , [| 0, 0, - `(c^2 * r - 2 * G * M) / c^2, 0 |]+   , [| 0, 0, 0, - (sin θ)^2 * `(c^2 * r - 2 * G * M) / c^2 |]    |]_#_# assertEqual "Christoffel symbols of the second kind Γ~3_#_#"   Γ~3_#_#
sample/math/geometry/riemann-curvature-tensor-of-T2.egi view
@@ -1,25 +1,25 @@-declare symbol a, b, θ, φ: MathExpr+declare symbol a, b, θ, φ: MathValue  -- Parameters-def x : Vector MathExpr := [| θ, φ |]+def x : Vector MathValue := [| θ, φ |] -def X : Vector MathExpr := [| `(a * cos θ + b) * cos φ -- x+def X : Vector MathValue := [| `(a * cos θ + b) * cos φ -- x           , `(a * cos θ + b) * sin φ -- y           , a * sin θ                -- z           |] -def e_i_j : Matrix MathExpr := ∂/∂ X_j x~i+def e_i_j : Matrix MathValue := ∂/∂ X_j x~i  -- Metric tensors-def g[_i_j] : Matrix MathExpr := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [2, 2]-def g[~i~j] : Matrix MathExpr := M.inverse g_#_#+def g[_i_j] : Matrix MathValue := generateTensor (\[x, y] -> V.* e_x_# e_y_#) [2, 2]+def g[~i~j] : Matrix MathValue := M.inverse g_#_#  assertEqual "Metric tensor"   g_#_#   [| [| a^2, 0 |], [| 0, `(a * cos θ + b)^2 |] |]_#_# assertEqual "Metric tensor"   g~#~#-  [| [| 1 / a^2, 0 |], [| 0, 1 / `(a * cos θ + b)^2 |] |]~#~#+  [| [| a^(-2), 0 |], [| 0, `(a * cos θ + b)^(-2) |] |]~#~#  -- Christoffel symbols def Γ_i[_j_k] := (1 / 2) * (∂/∂ g_i_k x~j + ∂/∂ g_i_j x~k - ∂/∂ g_j_k x~i)
sample/math/geometry/surface.egi view
@@ -1,30 +1,31 @@ -- Surface Geometry: First and Second Fundamental Forms -declare symbol x, y, f+declare symbol x, y+def f := function (x, y)  def v1 := [|1, 0, ∂/∂ (f x y) x|] def v2 := [|0, 1, ∂/∂ (f x y) y|]  assertEqual "tangent vector v1"   v1-  [| 1, 0, f|1 x y |]+  [| 1, 0, (userRefs f [1]) x y |]  assertEqual "tangent vector v2"   v2-  [| 0, 1, f|2 x y |]+  [| 0, 1, (userRefs f [2]) x y |]  def v3 := crossProduct v1 v2  assertEqual "normal vector (cross product)"   v3-  [| - f|1 x y, - f|2 x y, 1 |]+  [| - (userRefs f [1]) x y, - (userRefs f [2]) x y, 1 |]  def e3 := v3 / sqrt '(V.* v3 v3)  -- Unit normal vector assertEqual "unit normal vector e3"   e3-  [| - f|1 x y / sqrt ((f|1 x y)^2 + (f|2 x y)^2 + 1), - f|2 x y / sqrt ((f|1 x y)^2 + (f|2 x y)^2 + 1), 1 / sqrt ((f|1 x y)^2 + (f|2 x y)^2 + 1) |]+  [| - (userRefs f [1]) x y / sqrt (((userRefs f [1]) x y)^2 + ((userRefs f [2]) x y)^2 + 1), - (userRefs f [2]) x y / sqrt (((userRefs f [1]) x y)^2 + ((userRefs f [2]) x y)^2 + 1), 1 / sqrt (((userRefs f [1]) x y)^2 + ((userRefs f [2]) x y)^2 + 1) |]  -- First fundamental form coefficients def E := V.* v1 v1@@ -33,15 +34,15 @@  assertEqual "E (first fundamental form)"   E-  (1 + (f|1 x y)^2)+  (1 + ((userRefs f [1]) x y)^2)  assertEqual "F (first fundamental form)"   F-  (f|1 x y * f|2 x y)+  ((userRefs f [1]) x y * (userRefs f [2]) x y)  assertEqual "G (first fundamental form)"   G-  (1 + (f|2 x y)^2)+  (1 + ((userRefs f [2]) x y)^2)  -- Second fundamental form coefficients def L := V.* (∂/∂ v1 x) e3@@ -50,19 +51,19 @@  assertEqual "L (second fundamental form)"   L-  (f|1|1 x y / sqrt ((f|1 x y)^2 + (f|2 x y)^2 + 1))+  ((userRefs f [1, 1]) x y / sqrt (((userRefs f [1]) x y)^2 + ((userRefs f [2]) x y)^2 + 1))  assertEqual "M (second fundamental form)"   M-  (f|1|2 x y / sqrt ((f|1 x y)^2 + (f|2 x y)^2 + 1))+  ((userRefs f [1, 2]) x y / sqrt (((userRefs f [1]) x y)^2 + ((userRefs f [2]) x y)^2 + 1))  assertEqual "N (second fundamental form)"   N-  (f|2|2 x y / sqrt ((f|1 x y)^2 + (f|2 x y)^2 + 1))+  ((userRefs f [2, 2]) x y / sqrt (((userRefs f [1]) x y)^2 + ((userRefs f [2]) x y)^2 + 1))  -- Gaussian curvature K and mean curvature H def K := (L * N - M ^ 2) / '(E * G - F ^ 2)-def H := ('E * N + 'G * L + (-2) * F * M) / 2 * '(E * G - F ^ 2)+def H := ('E * N + 'G * L + (-2) * F * M) / 2 * `(E * G - F ^ 2)  -- The formulas for K and H involve complex expressions with partial derivatives -- They represent the Gaussian and mean curvatures of the surface z = f(x, y)
sample/math/geometry/thurston-non-sym.egi view
@@ -20,6 +20,18 @@  def β := `(1 + θ₂ - θ₂^2) +-- Sanity check on the metric definition (the rest of the computation+-- reaches S which currently hits a pre-existing pattern-matching bug).+assertEqual "g_3_3"+  g_3_3+  (κ / sqrt `(- θ₂^2 + θ₂ + 1))+assertEqual "g~3~3"+  g~3~3+  (`(1 + θ₂) / (κ * sqrt `(- θ₂^2 + θ₂ + 1)))+assertEqual "β"+  β+  `(- θ₂^2 + θ₂ + 1)+ def Γ~c_a_b := withSymbols [e]   (1 / 2) * g~c~e . (∂/∂ g_b_e x~a + ∂/∂ g_a_e x~b - ∂/∂ g_a_b x~e) @@ -59,7 +71,7 @@        | _        -> 0)     [5, 5] -def R'_i_j_k~l :=+def R'_i_j_k~l : Tensor MathValue :=   generateTensor     (\match as list integer with        | [#1, #1, _, _] -> 0@@ -84,7 +96,16 @@       sum (map (\σ -> R'_(σ 1)_j_1~i . R'_(σ 2)_(σ 3)_k~j . R'_(σ 4)_(σ 5)_i~k) es) -       sum (map (\σ -> R'_(σ 1)_j_1~i . R'_(σ 2)_(σ 3)_k~j . R'_(σ 4)_(σ 5)_i~k) os) -S+-- WCS (Wodzicki-Chern-Simons) invariant on the Thurston example (Section 4+-- of "Diffeomorphism Groups of Circle Bundles over Integral Symplectic+-- Manifolds"), non-symmetric ∇J variant. The Wolfram-simplified form of S,+-- with β = 1+θ₂-θ₂², is:+--   S = p² κ (-25 - 640 p² β² + 3072 p⁴ β⁴) / (16 β⁴)+-- See <https://github.com/egisatoshi/EMR-Paper-Computation>.+assertEqual "WCS invariant S"+  S+  (p^2 * κ * (- 25 - 640 * p^2 * β^2 + 3072 * p^4 * β^4) / (16 * β^4))+ -- After 10 seconds calculation, we can get the following result: -- (1536 p^6 κ Sqrt[(1 + θ₂ - θ₂^2)]^16 - 1536 p^6 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^14 - 576 p^4 (1 + θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^12 + 1536 p^6 (1 + θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^14 + 8 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^3 θ₂^2 κ - 88 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^2 θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 48 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 12 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^2 θ₂^4 κ - 24 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^2 + 288 p^4 (1 - 2 θ₂)^2 (1 + θ₂)^2 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 - 160 p^2 (1 - 2 θ₂) (1 + θ₂) θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ + 128 p^2 (1 - 2 θ₂)^2 (1 + θ₂) θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ - 48 p^2 (1 - 2 θ₂)^3 (1 + θ₂) θ₂^5 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 80 p^2 (1 - 2 θ₂)^2 (1 + θ₂) θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 768 p^4 (1 - 2 θ₂) (1 + θ₂) θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ + 8 p^2 (1 - 2 θ₂)^4 (1 + θ₂) θ₂^6 κ + 24 p^2 (1 - 2 θ₂)^3 (1 + θ₂) θ₂^4 κ Sqrt[(1 + θ₂ - θ₂^2)]^2 - 288 p^4 (1 - 2 θ₂)^2 (1 + θ₂) θ₂^4 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 + 112 p^2 (1 - 2 θ₂) (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^2 κ + 20 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^2 κ - 64 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^4 κ + 96 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^5 (1 - 2 θ₂) κ - 56 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^6 (1 - 2 θ₂)^2 κ - 80 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^4 (1 - 2 θ₂) κ + 384 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^4 κ + 16 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^2 θ₂^7 (1 - 2 θ₂)^3 κ + 40 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^5 (1 - 2 θ₂)^2 κ - 384 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^5 (1 - 2 θ₂) κ + 32 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^3 κ + 24 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^3 (1 - 2 θ₂) κ - 448 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^3 κ - 2 p^2 (1 - 2 θ₂)^4 θ₂^8 κ - 8 p^2 (1 - 2 θ₂)^3 θ₂^6 κ Sqrt[(1 + θ₂ - θ₂^2)]^2 + 96 p^4 (1 - 2 θ₂)^2 θ₂^6 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 - 10 p^2 (1 - 2 θ₂)^2 θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ - 16 p^2 (1 + θ₂)^3 (1 - 2 θ₂)^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 θ₂ κ + 64 p^2 (1 + θ₂)^2 (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂ κ + 40 p^2 (1 + θ₂)^2 (1 - 2 θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂ κ - 384 p^4 (1 + θ₂)^2 (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 κ θ₂ + 96 p^2 (1 + θ₂) θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ - 32 p^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂ κ - 24 p^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂ (1 - 2 θ₂) κ + 448 p^4 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^10 κ θ₂ - 32 p^2 (1 - 2 θ₂) (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ - 10 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 8 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ + 16 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^3 κ Sqrt[(1 + θ₂ - θ₂^2)]^4 - 2 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^4 κ - 96 p^4 (1 - 2 θ₂)^2 (1 + θ₂)^3 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 + 4 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 (1 + θ₂) (1 - 2 θ₂) κ + 8 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 (1 + θ₂) κ - 112 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 + θ₂) - 8 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^2 κ - 4 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^2 (1 - 2 θ₂) κ + 112 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^2 κ - 48 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂ κ + 40 p^2 (1 - 2 θ₂)^3 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^2 κ + 12 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 κ - 20 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 112 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^2 κ + 32 p^2 (1 - 2 θ₂) (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 κ - 32 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ + 40 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂ κ - 21 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^2 θ₂^2 κ + 7 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 80 p^2 (1 - 2 θ₂)^3 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^3 κ + 21 p^2 (1 - 2 θ₂)^4 (1 + θ₂) θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 32 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^2 (1 - 2 θ₂) κ + 48 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^3 (1 - 2 θ₂)^2 κ - 16 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^2 κ + 64 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^3 (1 - 2 θ₂) κ - 80 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^4 (1 - 2 θ₂)^2 κ + 40 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^5 (1 - 2 θ₂)^3 κ - 20 p^2 (1 - 2 θ₂)^3 θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ - 12 p^2 (1 - 2 θ₂)^2 θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ - 7 p^2 (1 - 2 θ₂)^4 θ₂^6 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 64 p^4 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ - 32 p^2 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ + 16 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 (1 + θ₂) κ - 64 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 (1 - 2 θ₂) θ₂ (1 + θ₂) κ + 576 p^4 θ₂ Sqrt[(1 + θ₂ - θ₂^2)]^12 κ - 144 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^12 κ - 224 p^4 (1 - 2 θ₂)^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^10 (1 + θ₂) - 384 p^4 κ (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^12 θ₂ + 224 p^4 (1 - 2 θ₂)^2 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^10 + 192 p^4 (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^12 κ + 128 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^14 κ - 320 p^4 θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 + θ₂) + 128 p^4 (1 - 2 θ₂) (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^10 κ - 128 p^4 θ₂^2 (1 - 2 θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^10 + 192 p^4 (1 + θ₂)^2 (1 - 2 θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^8 - 384 p^4 θ₂^2 (1 - 2 θ₂) (1 + θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^8 + 192 p^4 θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ (1 - 2 θ₂) - 256 p^4 θ₂ Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 - 2 θ₂) (1 + θ₂) + 256 p^4 θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 - 2 θ₂) + 128 p^4 θ₂^2 (1 - 2 θ₂)^2 κ (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 - 64 p^4 θ₂^4 (1 - 2 θ₂)^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^8 - 64 p^4 (1 - 2 θ₂)^2 (1 + θ₂)^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^8)/(16 Sqrt[(1 + θ₂ - θ₂^2)]^16) -- The above result is simplified using the Wolfam language as follows:
sample/math/geometry/thurston.egi view
@@ -6,6 +6,8 @@  def x~i := [| θ₁, θ₂, θ₃, θ₄ |]~i +def β := `(1 + θ₂ - θ₂^2)+ def g_i_j :=   [|[| 1, 0, 0, 0 |],     [| 0, 1, 0, 0 |],@@ -18,7 +20,16 @@     [| 0, 0, `(1 + θ₂) / (κ * (sqrt β)), θ₂ / ((sqrt β) * κ) |],     [| 0, 0, θ₂ / ((sqrt β) * κ), 1 / ((sqrt β) * κ) |]|] -def β := `(1 + θ₂ - θ₂^2)+-- Sanity check on the metric definition.+assertEqual "g_3_3"+  g_3_3+  (κ / sqrt `(- θ₂^2 + θ₂ + 1))+assertEqual "g~3~3"+  g~3~3+  (`(1 + θ₂) / (κ * sqrt `(- θ₂^2 + θ₂ + 1)))+assertEqual "β"+  β+  `(- θ₂^2 + θ₂ + 1)  def Γ~c_a_b := withSymbols [e]   (1 / 2) * g~c~e . (∂/∂ g_b_e x~a + ∂/∂ g_a_e x~b - ∂/∂ g_a_b x~e)@@ -34,15 +45,21 @@     [| 0, 0, 0, κ |],     [| 0, 0, -1 * κ, 0 |]|] -def J_a~c := J_a_b . g~b~c--def ∇_c T~(a_1)...~(a_r)_(b_1)..._(b_k) :=-  ∂/∂ T~(a_1)...~(a_r)_(b_1)..._(b_k) x~c-  + sum (map (\i -> Γ~(a_i)_d_c . T~(a_1)...~(a_(i-1))~d~(a_(i+1))...~(a_r)_(b_1)..._(b_k)) [1..r])-  - sum (map (\i -> Γ~d_(b_i)_c . T~(a_1)...~(a_r)_(b_1)..._(b_(i-1))_d_(b_(i+1))..._(b_k)) [1..k])+def J_a~c := withSymbols [b] J_a_b . g~b~c +-- The original EMR Thurston example used an inline ∇J definition (rather+-- than going through an abstracted variadic `∇_c T~(a_1)...~(a_r)_(b_1)..._(b_k)`).+-- The abstracted form caused type-inference errors (the variadic-index+-- pattern doesn't unify with the concrete monomorphic Tensor types),+-- preventing the file from compiling. Inline form matches the original+-- paper's expected S value (same simplified result).+--+-- Sign convention follows the EMR original (`+` Γ terms). The standard+-- textbook ∇ has `-` for lower indices and `+` for upper, but for the+-- WCS invariant the absolute sign cancels out in the product. def ∇J_m_a_b :=-    ∇_m J_a_b -- ∂/∂ J_a_b x~m - Γ~n_m_a . J_n_b - Γ~n_m_b . J_a_n+  withSymbols [n]+    ∂/∂ J_a_b x~m + Γ~n_m_a . J_n_b + Γ~n_m_b . J_a_n  def ∇J~m_a_b :=   withSymbols [t]@@ -63,7 +80,7 @@        | [_, _] -> 0)     [5, 5] -def R'{_i_j}_k~l :=+def R'{_i_j}_k~l : Tensor MathValue :=   generateTensor     (\match as list integer with        | [#1, #1, _, _] -> 0@@ -88,8 +105,34 @@       sum (map (\σ -> R'_(σ 1)_j_1~i . R'_(σ 2)_(σ 3)_k~j . R'_(σ 4)_(σ 5)_i~k) es) -       sum (map (\σ -> R'_(σ 1)_j_1~i . R'_(σ 2)_(σ 3)_k~j . R'_(σ 4)_(σ 5)_i~k) os) -S--- After 10 seconds calculation, we can get the following result:+-- WCS (Wodzicki-Chern-Simons) invariant on the Thurston example (Section 4+-- of "Diffeomorphism Groups of Circle Bundles over Integral Symplectic+-- Manifolds"). The Wolfram-simplified form of S, with β = 1+θ₂-θ₂², is:+--   S = p² κ (-25 - 640 p² β² + 3072 p⁴ β⁴) / (16 β⁴)+-- See <https://github.com/egisatoshi/EMR-Paper-Computation>.+--+-- The identity needs the defining relations of the quoted atoms+-- (β = 1+θ₂-θ₂² and `(1+θ₂) = 1+θ₂), which a structural comparison+-- cannot see.  Clear the Laurent denominators and reduce the difference+-- modulo the Groebner basis of the quote relations, applied once at the+-- point of comparison (declaring the relations as always-on rules would+-- blow up the intermediate computation).+def sExpected := p^2 * κ * (- 25 - 640 * p^2 * β^2 + 3072 * p^4 * β^4) / (16 * β^4)+def quoteGb := groebnerBasis ['(1 + θ₂ - θ₂^2 - β), '(1 + θ₂ - `(1 + θ₂))]++assertEqual "WCS invariant S"+  (polyNF quoteGb (16 * β^8 * (S - sExpected)))+  0++-- The same identity through plain rational arithmetic: expanding the+-- quotes makes both sides rational functions of θ₂, which polynomial+-- GCD reduces.+assertEqual "WCS invariant S (expanded)"+  ((expandAll S) = (expandAll sExpected))+  True++-- For reference, the raw (pre-reduction) result of the original EMR+-- computation was: -- (1536 p^6 κ Sqrt[(1 + θ₂ - θ₂^2)]^16 - 1536 p^6 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^14 - 576 p^4 (1 + θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^12 + 1536 p^6 (1 + θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^14 + 8 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^3 θ₂^2 κ - 88 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^2 θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 48 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 12 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^2 θ₂^4 κ - 24 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^2 + 288 p^4 (1 - 2 θ₂)^2 (1 + θ₂)^2 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 - 160 p^2 (1 - 2 θ₂) (1 + θ₂) θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ + 128 p^2 (1 - 2 θ₂)^2 (1 + θ₂) θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ - 48 p^2 (1 - 2 θ₂)^3 (1 + θ₂) θ₂^5 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 80 p^2 (1 - 2 θ₂)^2 (1 + θ₂) θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 768 p^4 (1 - 2 θ₂) (1 + θ₂) θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ + 8 p^2 (1 - 2 θ₂)^4 (1 + θ₂) θ₂^6 κ + 24 p^2 (1 - 2 θ₂)^3 (1 + θ₂) θ₂^4 κ Sqrt[(1 + θ₂ - θ₂^2)]^2 - 288 p^4 (1 - 2 θ₂)^2 (1 + θ₂) θ₂^4 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 + 112 p^2 (1 - 2 θ₂) (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^2 κ + 20 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^2 κ - 64 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^4 κ + 96 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^5 (1 - 2 θ₂) κ - 56 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^6 (1 - 2 θ₂)^2 κ - 80 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^4 (1 - 2 θ₂) κ + 384 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^4 κ + 16 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^2 θ₂^7 (1 - 2 θ₂)^3 κ + 40 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^5 (1 - 2 θ₂)^2 κ - 384 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^5 (1 - 2 θ₂) κ + 32 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^3 κ + 24 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^3 (1 - 2 θ₂) κ - 448 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^3 κ - 2 p^2 (1 - 2 θ₂)^4 θ₂^8 κ - 8 p^2 (1 - 2 θ₂)^3 θ₂^6 κ Sqrt[(1 + θ₂ - θ₂^2)]^2 + 96 p^4 (1 - 2 θ₂)^2 θ₂^6 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 - 10 p^2 (1 - 2 θ₂)^2 θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ - 16 p^2 (1 + θ₂)^3 (1 - 2 θ₂)^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 θ₂ κ + 64 p^2 (1 + θ₂)^2 (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂ κ + 40 p^2 (1 + θ₂)^2 (1 - 2 θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂ κ - 384 p^4 (1 + θ₂)^2 (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 κ θ₂ + 96 p^2 (1 + θ₂) θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ - 32 p^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂ κ - 24 p^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂ (1 - 2 θ₂) κ + 448 p^4 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^10 κ θ₂ - 32 p^2 (1 - 2 θ₂) (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ - 10 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 8 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ + 16 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^3 κ Sqrt[(1 + θ₂ - θ₂^2)]^4 - 2 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^4 κ - 96 p^4 (1 - 2 θ₂)^2 (1 + θ₂)^3 κ Sqrt[(1 + θ₂ - θ₂^2)]^6 + 4 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 (1 + θ₂) (1 - 2 θ₂) κ + 8 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 (1 + θ₂) κ - 112 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 + θ₂) - 8 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^2 κ - 4 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^2 (1 - 2 θ₂) κ + 112 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^2 κ - 48 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂ κ + 40 p^2 (1 - 2 θ₂)^3 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^2 κ + 12 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 κ - 20 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ + 112 p^2 (1 - 2 θ₂)^2 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^2 κ + 32 p^2 (1 - 2 θ₂) (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 κ - 32 p^2 (1 - 2 θ₂)^2 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ + 40 p^2 (1 - 2 θ₂)^3 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂ κ - 21 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^2 θ₂^2 κ + 7 p^2 (1 - 2 θ₂)^4 (1 + θ₂)^3 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 80 p^2 (1 - 2 θ₂)^3 (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^3 κ + 21 p^2 (1 - 2 θ₂)^4 (1 + θ₂) θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 32 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^2 (1 - 2 θ₂) κ + 48 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^3 (1 - 2 θ₂)^2 κ - 16 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 θ₂^2 κ + 64 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 θ₂^3 (1 - 2 θ₂) κ - 80 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 θ₂^4 (1 - 2 θ₂)^2 κ + 40 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^4 θ₂^5 (1 - 2 θ₂)^3 κ - 20 p^2 (1 - 2 θ₂)^3 θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^4 κ - 12 p^2 (1 - 2 θ₂)^2 θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^6 κ - 7 p^2 (1 - 2 θ₂)^4 θ₂^6 Sqrt[(1 + θ₂ - θ₂^2)]^2 κ - 64 p^4 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ - 32 p^2 (1 + θ₂)^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ + 16 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 (1 + θ₂) κ - 64 p^2 Sqrt[(1 + θ₂ - θ₂^2)]^8 (1 - 2 θ₂) θ₂ (1 + θ₂) κ + 576 p^4 θ₂ Sqrt[(1 + θ₂ - θ₂^2)]^12 κ - 144 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^12 κ - 224 p^4 (1 - 2 θ₂)^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^10 (1 + θ₂) - 384 p^4 κ (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^12 θ₂ + 224 p^4 (1 - 2 θ₂)^2 θ₂^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^10 + 192 p^4 (1 - 2 θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^12 κ + 128 p^4 Sqrt[(1 + θ₂ - θ₂^2)]^14 κ - 320 p^4 θ₂^2 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 + θ₂) + 128 p^4 (1 - 2 θ₂) (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^10 κ - 128 p^4 θ₂^2 (1 - 2 θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^10 + 192 p^4 (1 + θ₂)^2 (1 - 2 θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^8 - 384 p^4 θ₂^2 (1 - 2 θ₂) (1 + θ₂) κ Sqrt[(1 + θ₂ - θ₂^2)]^8 + 192 p^4 θ₂^4 Sqrt[(1 + θ₂ - θ₂^2)]^8 κ (1 - 2 θ₂) - 256 p^4 θ₂ Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 - 2 θ₂) (1 + θ₂) + 256 p^4 θ₂^3 Sqrt[(1 + θ₂ - θ₂^2)]^10 κ (1 - 2 θ₂) + 128 p^4 θ₂^2 (1 - 2 θ₂)^2 κ (1 + θ₂) Sqrt[(1 + θ₂ - θ₂^2)]^8 - 64 p^4 θ₂^4 (1 - 2 θ₂)^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^8 - 64 p^4 (1 - 2 θ₂)^2 (1 + θ₂)^2 κ Sqrt[(1 + θ₂ - θ₂^2)]^8)/(16 Sqrt[(1 + θ₂ - θ₂^2)]^16) -- The above result is simplified using the Wolfam language as follows: -- (p^2 (-25 - 640 p^2 (1 + θ₂- θ₂^2)^2 + 3072 p^4 (1 + θ₂ - θ₂^2)^4) κ) / (16 (1 + θ₂ - θ₂^2)^4)
sample/math/geometry/wedge-product.egi view
@@ -2,11 +2,11 @@ -- Wedge Product -- -declare symbol x, y, z: MathExpr+declare symbol x, y, z: MathValue  def N : Integer := 3 -def params : Vector MathExpr := [|x, y, z|]+def params : Vector MathValue := [|x, y, z|]  def g : Matrix Integer := [|[|1, 0, 0|], [|0, 1, 0|], [|0, 0, 1|]|] 
sample/math/geometry/yang-mills-equation-of-U1-gauge-theory.egi view
@@ -8,17 +8,17 @@  def g := [|[|-1, 0, 0, 0|], [|0, 1, 0, 0|], [|0, 0, 1, 0|], [|0, 0, 0, 1|]|] -def d (X : Tensor MathExpr) : Tensor MathExpr :=+def d (X : Tensor MathValue) : Tensor MathValue :=   !(flip ∂/∂) [| t, x, y, z |] X -def hodge (A : DiffForm MathExpr) : DiffForm MathExpr :=+def hodge (A : DiffForm MathValue) : DiffForm MathValue :=   let k := dfOrder A    in withSymbols [i, j]         sqrt (abs (M.det g_#_#)) *         foldl           (.)-          ((subrefs A (map 1#j_$1 (between 1 k))) . (subrefs (ε' N k) (map 1#i_$1 (between 1 N))))-          (map (\n -> g~(i_n)~(j_n)) (between 1 k))+          ((ε' N k)_(i_1)..._(i_N) . A..._(j_1)..._(j_k))+          (map (\n -> g~(i_n)~(j_n)) [1..k])  def δ A :=   let r := dfOrder A
sample/math/number/17th-root-of-unity.egi view
@@ -1,22 +1,22 @@-def z : MathExpr := rtu 17+def z : MathValue := rtu 17 -def a1 : MathExpr := z ^ 1 + z ^ 16-def a2 : MathExpr := z ^ 2 + z ^ 15-def a3 : MathExpr := z ^ 3 + z ^ 14-def a4 : MathExpr := z ^ 4 + z ^ 13-def a5 : MathExpr := z ^ 5 + z ^ 12-def a6 : MathExpr := z ^ 6 + z ^ 11-def a7 : MathExpr := z ^ 7 + z ^ 10-def a8 : MathExpr := z ^ 8 + z ^ 9+def a1 : MathValue := z ^ 1 + z ^ 16+def a2 : MathValue := z ^ 2 + z ^ 15+def a3 : MathValue := z ^ 3 + z ^ 14+def a4 : MathValue := z ^ 4 + z ^ 13+def a5 : MathValue := z ^ 5 + z ^ 12+def a6 : MathValue := z ^ 6 + z ^ 11+def a7 : MathValue := z ^ 7 + z ^ 10+def a8 : MathValue := z ^ 8 + z ^ 9 -def b11 : MathExpr := a1 + a4-def b12 : MathExpr := a1 - a4+def b11 : MathValue := a1 + a4+def b12 : MathValue := a1 - a4 -def b21 : MathExpr := a2 + a8-def b22 : MathExpr := a2 - a8+def b21 : MathValue := a2 + a8+def b22 : MathValue := a2 - a8 -def b31 : MathExpr := a3 + a5-def b32 : MathExpr := a3 - a5+def b31 : MathValue := a3 + a5+def b32 : MathValue := a3 - a5  def b41 := a6 + a7 def b42 := a6 - a7
sample/math/number/5th-root-of-unity.egi view
@@ -2,53 +2,63 @@ -- This file has been auto-generated by egison-translator. -- -def z : MathExpr := rtu 5+def z : MathValue := rtu 5 -def a11 : MathExpr := z ^ 1 + z ^ 4+def a11 : MathValue := z ^ 1 + z ^ 4 -def a12 : MathExpr := z ^ 2 + z ^ 3+def a12 : MathValue := z ^ 2 + z ^ 3 -def b10 : MathExpr := a11 + a12+def b10 : MathValue := a11 + a12 -def b11 : MathExpr := a11 - a12+def b11 : MathValue := a11 - a12 -def b12 : MathExpr := a12 - a11+def b12 : MathValue := a12 - a11  assertEqual "b10" b10 (-1) -def b10' : MathExpr := b10+def b10' : MathValue := b10 -def b11' : MathExpr := sqrt (b11 ^ 2)+def b11' : MathValue := sqrt (b11 ^ 2) -def a11' : MathExpr := (b10' + b11') / 2+def a11' : MathValue := (b10' + b11') / 2 -def a12' : MathExpr := (b10' - b11') / 2+def a12' : MathValue := (b10' - b11') / 2 -def a21 : MathExpr := z ^ 1 - z ^ 4+def a21 : MathValue := z ^ 1 - z ^ 4 -def a22 : MathExpr := z ^ 2 - z ^ 3+def a22 : MathValue := z ^ 2 - z ^ 3 -def b20 : MathExpr := a21 + a22+def b20 : MathValue := a21 + a22 -def b21 : MathExpr := a21 - a22+def b21 : MathValue := a21 - a22 -def b22 : MathExpr := a22 - a21+def b22 : MathValue := a22 - a21 -def b20' : MathExpr := sqrt ((-3) + 4 * a12')+def b20' : MathValue := sqrt ((-3) + 4 * a12') -def b21' : MathExpr := sqrt ((-3) + 4 * a11')+def b21' : MathValue := sqrt ((-3) + 4 * a11') -def a21' : MathExpr := (b20' + b21') / 2+def a21' : MathValue := (b20' + b21') / 2 -def a22' : MathExpr := (b20' - b21') / 2+def a22' : MathValue := (b20' - b21') / 2 -def z1' : MathExpr := (a11' + a21') / 2+def z1' : MathValue := (a11' + a21') / 2  assertEqual   "5th-root-of-unity"    z1'   ((-1 + sqrt 5 + sqrt (-5 - 2 * sqrt 5) + sqrt (-5 + 2 * sqrt 5)) / 4) ---assertEqual "z1'^5 = 1"---  (z1'^5)---  1+-- The check that motivated the radical work: z1'^5 = 1.  After the+-- principal-branch normalization every sqrt atom has a positive+-- radicand, the real part collapses arithmetically, and the imaginary+-- residue vanishes modulo the pair-product relations of the radical+-- atoms, applied once at this comparison point via idealNF.+def radicalRels : [MathValue] :=+  [ '((sqrt (5 + 2 * sqrt 5))^2 - 5 - 2 * sqrt 5)+  , '((sqrt (5 - 2 * sqrt 5))^2 - 5 + 2 * sqrt 5)+  , '((sqrt (5 + 2 * sqrt 5)) * (sqrt (5 - 2 * sqrt 5)) - sqrt 5)+  , '((sqrt 5)^2 - 5)+  , '(i^2 + 1) ]++assertEqual "z1'^5 = 1" (idealNF radicalRels (z1'^5 - 1)) 0
sample/math/number/7th-root-of-unity.egi view
@@ -1,21 +1,21 @@ -- 7th root of unity -def z : MathExpr := rtu 7+def z : MathValue := rtu 7 -def a11 : MathExpr := z ^ 1 + z ^ 6-def a12 : MathExpr := z ^ 2 + z ^ 5-def a13 : MathExpr := z ^ 3 + z ^ 4+def a11 : MathValue := z ^ 1 + z ^ 6+def a12 : MathValue := z ^ 2 + z ^ 5+def a13 : MathValue := z ^ 3 + z ^ 4 -def b10 : MathExpr := a11 + a12 + a13-def b10' : MathExpr := b10+def b10 : MathValue := a11 + a12 + a13+def b10' : MathValue := b10  assertEqual "b10'" b10' (-1) -def b11 : MathExpr := a11 + w * a12 + w ^ 2 * a13-def b12 : MathExpr := a13 + w * a11 + w ^ 2 * a12-def b13 : MathExpr := a12 + w * a13 + w ^ 2 * a11+def b11 : MathValue := a11 + w * a12 + w ^ 2 * a13+def b12 : MathValue := a13 + w * a11 + w ^ 2 * a12+def b13 : MathValue := a12 + w * a13 + w ^ 2 * a11 -def b11' : MathExpr := rt 3 (b11 * b12 * b13)+def b11' : MathValue := rt 3 (b11 * b12 * b13)  -- b11' = rt 3 (14 + 21 * w) 
sample/math/number/eisenstein-primes.egi view
@@ -1,7 +1,7 @@ -- Eisenstein primes: primes in Z[w] where w = (-1 + sqrt(3)*i) / 2  -- Generate Eisenstein integers and their norms-def eisensteinNorms : [(MathExpr, MathExpr)] :=+def eisensteinNorms : [(MathValue, MathValue)] :=   map (\(x, y) -> (x + y * w, (x + y * w) * (x + y * w ^ 2)))       (matchAll take 10 nats as set integer with         | $x :: $y :: _ -> (x, y))@@ -12,7 +12,7 @@    (3 + w, 7), (1 + 4 * w, 13), (2 + 3 * w, 7), (3 + 2 * w, 7), (4 + w, 13)]  -- Filter to get Eisenstein primes (those with prime norm)-def eisensteinPrimes : [(MathExpr, MathExpr)] :=+def eisensteinPrimes : [(MathValue, MathValue)] :=   filter     (\(_, n) -> isPrime n)     (map (\(x, y) -> (x + y * w, (x + y * w) * (x + y * w ^ 2)))
+ sample/math/number/elliptic-curve-over-F7.egi view
@@ -0,0 +1,50 @@+--+-- Point counting and the group law on an elliptic curve over F7+--+-- The quotient mechanism (design/type-cas-quotient.md) makes Z/7Z a+-- first-class nominal ring: `declare cas-quotient` derives the homomorphic+-- ring operations with per-operation reduction and a type-dispatched+-- equality, and checks the congruence laws at declaration time.+--++declare cas-quotient Mod7 := Integer by (\n -> modulo n 7)++-- E : y^2 = x^3 + 2 over F7+def rhs (x : Mod7) : Mod7 := x * x * x + projMod7 2++def elems := map projMod7 (between 0 6)++-- affine points: pairs (x, y) with y^2 = x^3 + 2+def affineCount :=+  sum (map (\x -> length (filter (\y -> (y * y) == rhs x) elems)) elems)++assertEqual "affine points of y^2 = x^3 + 2 over F7" affineCount 8++-- the projective count adds the point at infinity+def pointCount := affineCount + 1+assertEqual "#E(F7) = 9" pointCount 9++-- Hasse bound: |#E(F7) - (7 + 1)| <= 2 sqrt 7 < 6+assertEqual "Hasse bound"+  ((pointCount >= 3) && (pointCount <= 13)) True++--+-- Group law: doubling P = (3, 1) along the tangent line.+-- Field inversion comes from Fermat's little theorem, a^(-1) = a^5 in F7 —+-- a non-homomorphic (pattern-2) operation defined directly on the quotient.+--++def inv7 (a : Mod7) : Mod7 := a * a * a * a * a++def px := projMod7 3+def py := projMod7 1+assertEqual "P = (3, 1) lies on E" ((py * py) == rhs px) True++-- lambda = (3 x^2) / (2 y),  x' = lambda^2 - 2 x,  y' = lambda (x - x') - y+def lam := (projMod7 3 * px * px) * inv7 (projMod7 2 * py)+def qx := lam * lam - projMod7 2 * px+def qy := lam * (px - qx) - py++assertEqual "2P = (3, 6)" ((qx == projMod7 3) && (qy == projMod7 6)) True+assertEqual "2P lies on E" ((qy * qy) == rhs qx) True+-- 2P = -P, so P has order 3 — consistent with #E(F7) = 9
sample/math/number/gaussian-primes.egi view
@@ -1,7 +1,7 @@ -- Gaussian primes: primes in Z[i]  -- Generate Gaussian integers and their norms-def gaussianNorms : [(MathExpr, MathExpr)] :=+def gaussianNorms : [(MathValue, MathValue)] :=   map (\(x, y) -> (x + y * i, (x + y * i) * (x - y * i)))       (matchAll take 10 nats as set integer with         | $x :: $y :: _ -> (x, y))@@ -12,7 +12,7 @@    (3 + i, 10), (1 + 4 * i, 17), (2 + 3 * i, 13), (3 + 2 * i, 13), (4 + i, 17)]  -- Filter to get Gaussian primes (those with prime norm)-def gaussianPrimes : [(MathExpr, MathExpr)] :=+def gaussianPrimes : [(MathValue, MathValue)] :=   filter     (\(_, n) -> isPrime n)     (map (\(x, y) -> (x + y * i, (x + y * i) * (x - y * i)))
sample/math/number/tribonacci.egi view
@@ -23,26 +23,29 @@   B   [| 1, 0, 0 |] +-- Matrix-vector multiplication comes from the library: M.* is+-- Matrix*Matrix (it indexes the right operand twice), MV.* contracts+-- on the single shared index. assertEqual "A * B"-  (M.* A B)+  (MV.* A B)   [| 1, 1, 0 |]  assertEqual "A^2 * B"-  (M.* (M.power A 2) B)+  (MV.* (M.power A 2) B)   [| 2, 1, 1 |]  assertEqual "A^3 * B"-  (M.* (M.power A 3) B)+  (MV.* (M.power A 3) B)   [| 4, 2, 1 |]  assertEqual "A^4 * B"-  (M.* (M.power A 4) B)+  (MV.* (M.power A 4) B)   [| 7, 4, 2 |]  assertEqual "A^5 * B"-  (M.* (M.power A 5) B)+  (MV.* (M.power A 5) B)   [| 13, 7, 4 |]  assertEqual "A^100 * B (100th tribonacci)"-  (M.* (M.power A 100) B)+  (MV.* (M.power A 100) B)   [| 180396380815100901214157639, 98079530178586034536500564, 53324762928098149064722658 |]
sample/physics/tension.egi view
@@ -4,15 +4,15 @@  declare symbol α, β, γ, c1, c2, p -def C : Matrix MathExpr := [|[|α, 0, 0|], [|0, β, 0|], [|0, 0, γ|]|]+def C : Matrix MathValue := [|[|α, 0, 0|], [|0, β, 0|], [|0, 0, γ|]|] -def I (C: Matrix MathExpr) : MathExpr := trace C+def I (C: Matrix MathValue) : MathValue := trace C -def II (C: Matrix MathExpr) : MathExpr := (trace C ^ 2 - trace (M.* C C)) / 2+def II (C: Matrix MathValue) : MathValue := (trace C ^ 2 - trace (M.* C C)) / 2 -def III (C: Matrix MathExpr) : MathExpr := M.det C+def III (C: Matrix MathValue) : MathValue := M.det C -def W : MathExpr := c1 * (I C - 3) + c2 * (II C - 3)+def W : MathValue := c1 * (I C - 3) + c2 * (II C - 3)  I C @@ -28,6 +28,6 @@  W -def S_i_j : Matrix MathExpr := 2 * ∂/∂ W C~i~j - p * (M.inverse C)_i_j+def S_i_j : Matrix MathValue := 2 * ∂/∂ W C~i~j - p * (M.inverse C)_i_j  S_#_#
sample/physics/tension2.egi view
@@ -4,20 +4,20 @@  declare symbol α, β, γ, c1, c2, p -def C : Matrix MathExpr := [|[|α, 0, 0|], [|0, β, 0|], [|0, 0, γ|]|]+def C : Matrix MathValue := [|[|α, 0, 0|], [|0, β, 0|], [|0, 0, γ|]|] -def I (C: Matrix MathExpr) : MathExpr := trace C+def I (C: Matrix MathValue) : MathValue := trace C -def II (C: Matrix MathExpr) : MathExpr := (trace C ^ 2 - trace (M.* C C)) / 2+def II (C: Matrix MathValue) : MathValue := (trace C ^ 2 - trace (M.* C C)) / 2 -def III (C: Matrix MathExpr) : MathExpr := M.det C+def III (C: Matrix MathValue) : MathValue := M.det C -def I' (C: Matrix MathExpr) : MathExpr := I C / III C ^ (1 / 3)+def I' (C: Matrix MathValue) : MathValue := I C / III C ^ (1 / 3) -def II' (C: Matrix MathExpr) : MathExpr := II C / III C ^ (2 / 3)+def II' (C: Matrix MathValue) : MathValue := II C / III C ^ (2 / 3) -def W : MathExpr := c1 * (I' C - 3) + c2 * (II' C - 3)+def W : MathValue := c1 * (I' C - 3) + c2 * (II' C - 3) -def S_i_j : Matrix MathExpr := 2 * ∂/∂ W C~i~j - p * (M.inverse C)_i_j+def S_i_j : Matrix MathValue := 2 * ∂/∂ W C~i~j - p * (M.inverse C)_i_j  substitute [(α, 1), (β, 1), (γ, 1)] S_#_#
sample/physics/tension3.egi view
@@ -4,20 +4,20 @@  declare symbol α, β, γ, c, p, l -def C : Matrix MathExpr := [|[|α, 0, 0|], [|0, β, 0|], [|0, 0, γ|]|]+def C : Matrix MathValue := [|[|α, 0, 0|], [|0, β, 0|], [|0, 0, γ|]|] -def I (C: Matrix MathExpr) : MathExpr := trace C+def I (C: Matrix MathValue) : MathValue := trace C -def II (C: Matrix MathExpr) : MathExpr := (trace C ^ 2 - trace (M.* C C)) / 2+def II (C: Matrix MathValue) : MathValue := (trace C ^ 2 - trace (M.* C C)) / 2 -def III (C: Matrix MathExpr) : MathExpr := M.det C+def III (C: Matrix MathValue) : MathValue := M.det C -def I' (C: Matrix MathExpr) : MathExpr := I C / III C ^ (1 / 3)+def I' (C: Matrix MathValue) : MathValue := I C / III C ^ (1 / 3) -def II' (C: Matrix MathExpr) : MathExpr := II C / III C ^ (2 / 3)+def II' (C: Matrix MathValue) : MathValue := II C / III C ^ (2 / 3) -def W : MathExpr := c_1 * (I' C - 3) + c_2 * (II' C - 3)+def W : MathValue := c_1 * (I' C - 3) + c_2 * (II' C - 3) -def S_i_j : Matrix MathExpr := 2 * ∂/∂ W C~i~j - p * (M.inverse C)_i_j+def S_i_j : Matrix MathValue := 2 * ∂/∂ W C~i~j - p * (M.inverse C)_i_j  expandAll (substitute [(α, l), (β, 1 / sqrt l), (γ, 1 / sqrt l)] S_#_#)
sample/poker-hands-with-joker.egi view
@@ -25,6 +25,7 @@     | Card $x $y -> [(x, y)]     | Joker -> matchAll ([Spade, Heart, Club, Diamond], [1..13]) as (set something, set something) with                | ($s :: _, $n :: _) -> (s, n)+    | _ -> []   -- unreachable (Card/Joker enumerate the type); satisfies arm exhaustiveness (Def 4.2(1c)), which cannot enumerate ADT constructors   | $ as something with     | $tgt -> [tgt] 
sample/rosetta/partial.egi view
@@ -4,13 +4,13 @@  def fs {a, b} : (a -> b) -> [a] -> [b] := 2#(map $1 $2) -def f1 {Num a} : a -> a := 1#($1 * 2)+def f1 {MulSemigroup a} : a -> a := 1#($1 * 2) -def f2 {Num a} : a -> a := 1#(power $1 2)+def f2 {MulSemigroup a} : a -> a := 1#(power $1 2) -def fsf1 {Num a} : [a] -> [a] := 1#(fs f1 $1)+def fsf1 {MulSemigroup a} : [a] -> [a] := 1#(fs f1 $1) -def fsf2 {Num a} : [a] -> [a] := 1#(fs f2 $1)+def fsf2 {MulSemigroup a} : [a] -> [a] := 1#(fs f2 $1)  fsf1 [0, 1, 2, 3] 
sample/salesman.egi view
@@ -4,9 +4,9 @@  def station : Matcher String := string -def price : Matcher Integer := integer+def price := integer -def graph : Matcher [(String, [(String, Integer)])] := multiset (station, multiset (station, price))+def graph := multiset (station, multiset (station, price))  def graphData : [(String, [(String, Integer)])] :=   [ ( "Tokyo"
sample/salesman2.egi view
@@ -4,9 +4,9 @@  def station : Matcher String := string -def price : Matcher Integer := integer+def price := integer -def graph : Matcher [(String, [(String, Integer)])] := multiset (station, multiset (station, price))+def graph := multiset (station, multiset (station, price))  def graphData : [(String, [(String, Integer)])] :=   [ ( "Berlin"
sample/sat/cdcl.egi view
@@ -9,11 +9,11 @@   | guessed (Integer, Integer)   | whichever (Integer, Integer) -def literal : Matcher Integer := integer+def literal := integer -def stage : Matcher Integer := integer+def stage := integer -def taggedLiteral : Matcher (Integer, Integer) := (literal, stage)+def taggedLiteral := (literal, stage)  def assignment : Matcher Assignment :=   matcher
sample/tree.egi view
@@ -1,4 +1,7 @@-def tree {a, b} (a: Matcher b) : Matcher (Tree b) := matcher+inductive Tree a := Leaf a | Node a [Tree a]+inductive pattern Tree a := leaf a | node a [Tree a]++def tree {a, b} (a: MatcherSlot b b) : Matcher (Tree b) := matcher   | leaf $ as a with     | Leaf $x -> [x]     | Node _ _ -> []
test/Test.hs view
@@ -1,8 +1,9 @@ module Main where  import           Control.Monad.IO.Class         (liftIO)-import           Control.Monad.Trans.Class      (lift)+import           Data.List                      (sort, (\\)) import           System.Environment             (getArgs)+import           System.FilePath.Glob           (glob) import           System.IO                      (hFlush, stdout)  import           Test.Framework                 (defaultMainWithArgs)@@ -10,46 +11,51 @@ import           Test.HUnit  import           Language.Egison-import           Language.Egison.AST            (TopExpr(..))-import           Language.Egison.MathOutput  main :: IO () main = do-  -- t <- evalRuntimeT defaultOption mathOutputTest   args <- getArgs-  flip defaultMainWithArgs args . hUnitTestToTests . test $ -    -- Skip mathOutputTest for now due to infinite loop-    map runTestCase testCases+  libTests <- discoverLibTests+  mapM_ (\(f, why) -> putStrLn ("Skipping " ++ f ++ " (" ++ why ++ ")"))+        skippedLibTests+  flip defaultMainWithArgs args . hUnitTestToTests . test $+    map runTestCase (languageTests ++ libTests ++ sampleTests) -testCases :: [FilePath]-testCases =+-- | Language-level tests: the surface syntax and the primitives.+languageTests :: [FilePath]+languageTests =   [ "test/syntax.egi"   , "test/primitive.egi"-  , "test/lib/core/assoc.egi"-  , "test/lib/core/base.egi"-  , "test/lib/core/collection.egi"-  , "test/lib/core/maybe.egi"-  , "test/lib/core/number.egi"-  , "test/lib/core/order.egi"-  , "test/lib/core/random.egi"-  , "test/lib/core/sort.egi"-  , "test/lib/core/string.egi"-  , "test/lib/math/algebra.egi"-  -- , "test/lib/math/analysis.egi"   -- Skipped due to infinite loop-  -- , "test/lib/math/arithmetic.egi"  -- Skipped due to infinite loop-  -- , "test/lib/math/tensor.egi"     -- Skipped due to infinite loop+  ] ---  , "sample/mahjong.egi" -- for testing pattern functions-  , "sample/primes.egi" -- for testing pattern matching with infinitely many results-  , "sample/sat/cdcl.egi" -- for testing a practical program using pattern matching+-- | Library unit tests: every test/lib/**/*.egi is discovered, so a new+-- suite dropped there runs without editing this file.  To exclude one,+-- add it to skippedLibTests with the reason.+discoverLibTests :: IO [FilePath]+discoverLibTests = do+  files <- glob "test/lib/**/*.egi"+  return (sort files \\ map fst skippedLibTests)++-- | Discovered files excluded from the run, with the reason recorded+-- (printed at startup so the exclusion stays visible in the log).+skippedLibTests :: [(FilePath, String)]+skippedLibTests =+  [ ("test/lib/core/io.egi",    "interactive IO demos; its helper functions no longer exist")+  , ("test/lib/core/shell.egi", "loads lib/core/shell.egi, which was removed")+  ]++-- | Whole programs registered for the language features they exercise.+sampleTests :: [FilePath]+sampleTests =+  [ "sample/primes.egi"                 -- pattern matching with infinitely many results+  , "sample/sat/cdcl.egi"               -- a practical pattern-matching program   , "sample/poker-hands.egi"   , "sample/poker-hands-with-joker.egi"--  , "sample/math/geometry/riemann-curvature-tensor-of-S2.egi" -- for testing tensor index notation-  , "sample/math/geometry/riemann-curvature-tensor-of-T2.egi" -- for testing tensor index notation and math quote-  , "sample/math/geometry/curvature-form.egi" -- for testing differential form-  , "sample/math/number/17th-root-of-unity.egi" -- for testing rewriting of mathematical expressions-  , "sample/math/geometry/hodge-laplacian-polar.egi" -- for testing "..." in tensor indices+  , "sample/math/geometry/riemann-curvature-tensor-of-S2.egi" -- tensor index notation+  , "sample/math/geometry/riemann-curvature-tensor-of-T2.egi" -- tensor indices and math quote+  , "sample/math/geometry/curvature-form.egi"                 -- differential forms+  , "sample/math/number/17th-root-of-unity.egi"               -- rewriting of mathematical expressions+  , "sample/math/geometry/hodge-laplacian-polar.egi"          -- "..." in tensor indices   ]  runTestCase :: FilePath -> Test@@ -59,45 +65,17 @@     putStrLn $ "\n=== Testing: " ++ file ++ " ==="     hFlush stdout   env <- initialEnv-  -- Load core libraries and math normalization library+  -- Load core libraries, the math normalization library, and the test+  -- file in ONE batch, mirroring the interpreter's initial load (see+  -- Interpreter/egison.hs: the test file is included in the initial+  -- load).  A separate batch would keep the library operators' closures+  -- pointing at the library-time mathNormalize, so rules declared in+  -- the test file (declare rule auto / declare ideal) would never fire.   let coreLibExprs = map Load coreLibraries       mathLibExpr = [Load "lib/math/normalize.egi"]       allLibExprs = coreLibExprs ++ mathLibExpr-  env' <- evalTopExprsNoPrint env allLibExprs-  -- Then load the test file   exprs <- loadFile file-  evalTopExprsNoPrint env' exprs+  evalTopExprsNoPrint env (allLibExprs ++ exprs)   where     assertEvalM :: EvalM a -> Assertion     assertEvalM m = fromEvalM defaultOption m >>= assertString . either show (const "")--mathOutputTest :: RuntimeM Test-mathOutputTest = do-  envResult <- fromEvalT $ do-    env <- initialEnv-    -- Load core libraries and math normalization library-    let coreLibExprs = map Load coreLibraries-        mathLibExpr = [Load "lib/math/normalize.egi"]-        allLibExprs = coreLibExprs ++ mathLibExpr-    evalTopExprsNoPrint env allLibExprs-  env <- case envResult of-    Left err -> error $ "Failed to initialize environment: " ++ show err-    Right e -> return e-  latexTest <- mathOutputTestLatex env-  return $ TestList [latexTest]--mathOutputTestLatex :: Env -> RuntimeM Test-mathOutputTestLatex env = do-  TestLabel "math output: latex" . TestList <$>-    mapM (\(x, y, z) -> makeTest x y z)-      [ ("div", "x / y", "\\frac{x}{y}")-      ]- where-   makeTest = makeMathOutputTest env "latex"--makeMathOutputTest :: Env -> String -> String -> String -> String -> RuntimeM Test-makeMathOutputTest env lang label expr expectedOutput = do-  res <- fromEvalT (runExpr env expr)-  case res of-    Left _    -> return . TestCase $ assertFailure "Failed to evaluate the expression"-    Right res -> return . TestCase $ assertEqual label ("#" ++ lang ++ "|" ++ expectedOutput ++ "|#") (prettyMath lang res)
test/lib/core/assoc.egi view
@@ -1,4 +1,4 @@-declare symbol x, y, z: MathExpr+declare symbol x, y, z: MathValue  assertEqual "toAssoc"   (toAssoc [x, x, y, z])@@ -17,22 +17,22 @@   [x, y]  assertEqual "assocMultiset"-  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset something with+  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset integer with     | (#z, $n) :: $r -> (n, r))   [(1, [(x, 3), (y, 2)])]  assertEqual "assocMultiset"-  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset something with+  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset integer with     | ($a, #2) :: $r -> (a, r))   [(x, [(x, 1), (y, 2), (z, 1)]), (y, [(x, 3), (z, 1)])]  assertEqual "assocMultiset"-  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset something with+  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset integer with     | (#y, #1) :: $r -> r)   [[(x, 3), (y, 1), (z, 1)]]  assertEqual "assocMultiset"-  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset something with+  (matchAll [(x, 3), (y, 2), (z, 1)] as assocMultiset integer with     | ($a, $n) :: $r -> (a, n, r))   [(x, 3, [(y, 2), (z, 1)]), (y, 2, [(x, 3), (z, 1)]), (z, 1, [(x, 3), (y, 2)])] 
test/lib/math/analysis.egi view
@@ -1,37 +1,71 @@ ----- This file has been auto-generated by egison-translator.+-- Originally auto-generated by egison-translator; modernized for the+-- typed CAS (declare symbol; expectations follow the current normal+-- forms: the Pythagorean auto rule rewrites cos^2 to 1 - sin^2,+-- casRewriteExp merges exp products and powers, and function-symbol+-- derivative marks are positional and sorted -- Schwarz canonical+-- form, so the mixed second-order Taylor terms merge into one). -- +declare symbol x, y, z, a+ assertEqual "d/d - case 1" (d/d (x ^ 2) x) (2 * x)  assertEqual "d/d - case 2" (d/d (a ^ (x ^ 2)) x) (2 * a ^ (x ^ 2) * log a * x) -assertEqual "d/d - case 3" (d/d (cos x * sin x) x) ((- (sin x ^ 2)) + cos x ^ 2)+assertEqual "d/d - case 3" (d/d (cos x * sin x) x) (1 - 2 * sin x ^ 2)  assertEqual   "d/d - case 4"   (d/d (sigmoid z) z)-  (exp (- z) / (1 + 2 * exp (- z) + exp (- z) ^ 2))+  (exp (- z) / (1 + 2 * exp (- z) + exp (-2 * z)))  assertEqual "d/d - case 5" (d/d (d/d (log x) x) x) ((-1) / x ^ 2)  assertEqual-  "tailor-expansion - case 1"+  "taylor-expansion - case 1"   (take 4 (taylorExpansion (e ^ (i * x)) x 0))   [1, i * x, -1 * x ^ 2 / 2, -1 * i * x ^ 3 / 6] +def f := function (x, y)+ assertEqual   "multivariate-tailor-expansion - case 1"   (take 3 (multivariateTaylorExpansion (f x y) [|x, y|] [|0, 0|]))   [ f 0 0-  , x * f|1 0 0 + y * f|2 0 0-  , (x ^ 2 * f|1|1 0 0 + x * y * f|1|2 0 0 + x * y * f|2|1 0 0 + y ^ 2 * f|2|2-                                                                           0-                                                                           0) / 2 ]+  , ((userRefs f [1]) 0 0) * x + ((userRefs f [2]) 0 0) * y+  , (1 / 2) * ((userRefs f [1, 1]) 0 0) * x ^ 2+      + ((userRefs f [1, 2]) 0 0) * x * y+      + (1 / 2) * ((userRefs f [2, 2]) 0 0) * y ^ 2 ]  assertEqual   "function expr"-  (let f := function (x, y)-    in d/d f y)-  (let f := function (x, y)-    in userRefs f [y])+  (let g := function (x, y)+    in d/d g y)+  (let g := function (x, y)+    in userRefs g [y])++assertEqual+  "analytic derivative keeps FunctionData and registered chain rules"+  (∂/∂ (sin (f ^ 2)) x)+  (2 * (userRefs f [1]) * f * cos (f ^ 2))++assertEqual+  "analytic derivative maps coordinate tensors"+  (∂/∂ f [|x, y|])+  [|userRefs f [1], userRefs f [2]|]++def derivativeVector := generateTensor (\[i] -> function (x, y)) [2]++def functionDataHead (value: MathValue) : MathValue :=+  match value as mathValue with+    | func $head _ -> head+    | _ -> 0++assertEqual+  "symbolIndices preserves component and derivative index kinds"+  (symbolIndices+    (functionDataHead+      (partialDiffMV+        (partialDiffMV derivativeVector_2 y) x)))+  [SubIndex 2, UserIndex 1, UserIndex 2]
test/lib/math/arithmetic.egi view
@@ -1,7 +1,12 @@ ----- This file has been auto-generated by egison-translator.+-- Originally auto-generated by egison-translator; modernized for the+-- typed CAS (declare symbol; sqrt expectations follow the current+-- engine: content split keeps rational radicands, and the pair rule+-- merges sqrt products -- see Math/Rewrite.hs casRewriteSqrt). -- +declare symbol x, y+ assertEqual "sum" (sum (take 5 nats)) 15  assertEqual "product" (product (take 5 nats)) 120@@ -14,9 +19,9 @@  assertEqual "gcd" (gcd 15 40) 5 -assertEqual "sqrt - case 1" (sqrt (50 * x ^ 2 / y)) (5 * x * sqrt (2 * y) / y)+assertEqual "sqrt - case 1" (sqrt (50 * x ^ 2 / y)) (5 * x * sqrt (2 / y))  assertEqual   "sqrt - case 2"   (sqrt (3 * x) * sqrt (2 * y))-  (sqrt 6 * sqrt x * sqrt y)+  (sqrt (6 * x * y))
+ test/lib/math/gcd.egi view
@@ -0,0 +1,38 @@+--+-- Multivariate polynomial GCD reduction (design/cas-simplification.md G1).+--+-- Stage 2 of the Poly/Poly fraction reduction: cancel the multivariate+-- polynomial GCD (subresultant PRS over the rationals), treating every+-- atom -- symbols, parameters, symbolic applications such as 'cos --+-- uniformly as a variable. Fail-open outside the supported shape.+--++declare symbol x, y, a, b, c, r, G, M, X, Y, θ++-- Basic multivariate cancellations.+assertEqual "linear factor" ((x * y + y^2) / (x + y)) y+assertEqual "square over factor" ((x + y)^2 / (x + y)) (x + y)+assertEqual "difference of squares" ((x^2 - y^2) / (x - y)) (x + y)++-- Schwarzschild-style: a common polynomial factor containing parameters+-- cancels; the remaining X / Y folds into Laurent form.+assertEqual "parametric common factor"+  (((c^2 * r - 2 * G * M) * X) / ((c^2 * r - 2 * G * M) * Y))+  (X / Y)++-- T2-style: the common factor contains an application atom (cos θ).+assertEqual "application atom factor"+  ((b + a * cos θ)^2 / (b + a * cos θ))+  (b + a * cos θ)++-- Rational coefficients are cleared by a common scale (value preserved).+assertEqual "rational coefficients" (((x + y) / 2) / (x + y)) (1 / 2)++-- Coprime fractions stay untouched.+assertEqual "coprime untouched"+  (show ((x^2 - y) / (x - 1)))+  "(x^2 - y) / (x - 1)"++-- Value preservation.+assertEqual "value preserved 1" ((x * y + y^2) / (x + y) - y) 0+assertEqual "value preserved 2" ((x^2 - y^2) / (x - y) - (x + y)) 0
+ test/lib/math/groebner.egi view
@@ -0,0 +1,89 @@+--+-- Groebner bases: the value-level engine of+-- lib/math/algebra/groebner.egi (design/cas-simplification.md G2).+-- The declare ideal declaration is tested separately in ideal.egi:+-- its rules are batch-scoped, so they would rewrite the radical+-- symbols in this file's value-level tests too.+--++declare symbol s2, s3, s6+declare symbol x, y, z+declare symbol θ, φ, α++-- completion: multiplication table of {1, √2, √3, √6}+def gb := groebnerBasis [s2^2 - 2, s3^2 - 3, s6 - s2 * s3]+assertEqual "multiplication table"+  (show gb)+  "[s2^2 - 2, s2 s3 - s6, s2 s6 - 2 * s3, s3^2 - 3, s3 s6 - 3 * s2, s6^2 - 6]"+assertEqual "completed rule sqrt2*sqrt6" (polyNF gb (s2 * s6)) (2 * s3)++-- normal form, membership, invariance+assertEqual "binomial square" (polyNF gb ((s2 + s3)^2)) (2 * s6 + 5)+assertEqual "ideal membership" (polyNF gb (s2 * s3 - s6)) 0+assertEqual "NF invariant under ideal shifts"+  (polyNF gb ((s2 + s3)^2 + (s2^2 - 2) * (s6 + 7)))+  (polyNF gb ((s2 + s3)^2))++-- textbook example: cyclic-3+def cyc := groebnerBasis [x + y + z, x*y + y*z + z*x, x*y*z - 1]+assertEqual "cyclic-3 reduced basis" (show cyc) "[z + y + x, y^2 + x^2 + x y, x^3 - 1]"+assertEqual "x^5 y mod cyclic-3" (polyNF cyc (x^5 * y)) (x^2 * y)++-- keep-prefix semantics of the explicit priority list+assertEqual "keep y" (polyNFWith [y] [x + y] (x^3)) (- y^3)+assertEqual "keep x" (polyNFWith [x] [x + y] (y^3)) (- x^3)++-- trigonometric atoms via the lib helper (rule-suppression quote inside)+assertEqual "trig generator survives construction"+  (show (trigIdeal θ)) "[('sin θ)^2 + ('cos θ)^2 - 1]"+assertEqual "sin^4 - cos^4, keep sin"+  (polyNFWith [('sin θ)] (trigIdeal θ) ((sin θ)^4 - (cos θ)^4))+  (2 * (sin θ)^2 - 1)++-- guards: fail-open on Laurent / fractions, trivial ideals+assertEqual "empty ideal" (groebnerBasis []) []+assertEqual "unit ideal" (groebnerBasis [5]) [1]+assertEqual "laurent fail-open" (groebnerBasis [x / y]) [x / y]+assertEqual "univariate power" (polyNF [x^2 - 2] (x^6)) 8++--+-- Observability and the safe one-call forms.+--++assertEqual "polyNFStatus ok" (polyNFStatus [x^2 - 2] (x^4)) ("ok", 4)+assertEqual "polyNFStatus fail-open"+  (fst (polyNFStatus [x^2 - 2] (x / y))) "fail-open"+assertEqual "idealNF completes its generators"+  (idealNF [s2^2 - 2, s3^2 - 3, s6 - s2 * s3] (s2 * s6)) (2 * s3)+assertEqual "idealEquals via one difference test"+  (idealEquals [s2^2 - 2, s3^2 - 3, s6 - s2 * s3] ((s2 + s3)^2) (2 * s6 + 5))+  True++--+-- The coefficient-field engine: GF(4) = F_2[α]/(α^2 + α + 1).+--++def red2 (c: MathValue) : MathValue := i.modulo c 2+def fdiv2 (a: MathValue) (b: MathValue) : MathValue := i.modulo (a * i.modulo b 2) 2++def gb4 := groebnerBasisField red2 fdiv2 [] [α^2 + α + 1]+assertEqual "GF(4) basis" (show gb4) "[α^2 + α + 1]"+assertEqual "GF(4): (α+1)^2 = α" (polyNFField red2 fdiv2 [] gb4 ((α + 1)^2)) α+assertEqual "GF(4): α (α+1) = 1" (polyNFField red2 fdiv2 [] gb4 (α * (α + 1))) 1+assertEqual "GF(4): α^3 = 1" (polyNFField red2 fdiv2 [] gb4 (α^3)) 1++--+-- The four-term fifth root of unity: the real part of z4^5 collapses+-- to 1 by arithmetic (principal-branch atoms), and the imaginary+-- residue vanishes modulo the pair-product relations of the+-- (all-positive-radicand) atoms.+--++def z4 := (-1 + (sqrt 5) + sqrt(-5 - 2*(sqrt 5)) + sqrt(-5 + 2*(sqrt 5))) / 4+def radicalRels :=+  [ '((sqrt (5 + 2 * sqrt 5))^2 - 5 - 2 * sqrt 5)+  , '((sqrt (5 - 2 * sqrt 5))^2 - 5 + 2 * sqrt 5)+  , '((sqrt (5 + 2 * sqrt 5)) * (sqrt (5 - 2 * sqrt 5)) - sqrt 5)+  , '((sqrt 5)^2 - 5)+  , '(i^2 + 1) ]+assertEqual "z4^5 = 1" (idealNF radicalRels (z4^5 - 1)) 0
+ test/lib/math/ideal.egi view
@@ -0,0 +1,27 @@+--+-- declare ideal (design/cas-simplification.md G3): Groebner completion+-- at declaration time.  The generated term-level rules are batch-scoped+-- (like declare rule auto), so they apply to every expression in this+-- file -- which is why these tests live apart from the value-level+-- groebner.egi tests.+--++declare symbol s2, s3, s6+declare symbol θ, φ++declare ideal [s2^2 - 2, s3^2 - 3, s6 - s2 * s3]++assertEqual "square" (s2^2) 2+assertEqual "product folds" (s2 * s3) s6+assertEqual "completed rule" (s2 * s6) (2 * s3)+assertEqual "binomial square" ((s2 + s3)^2) (2 * s6 + 5)+assertEqual "conjugate product" ((s2 + s3) * (s2 - s3)) (-1)++-- Pythagorean ideal on the trig atoms; sin θ appears first in the+-- generator, so sin survives (appearance-order priority).+declare ideal [(sin θ)^2 + (cos θ)^2 - 1]++assertEqual "sin^4 - cos^4" ((sin θ)^4 - (cos θ)^4) (2 * (sin θ)^2 - 1)+assertEqual "sin^2 + 2 cos^2" ((sin θ)^2 + 2 * (cos θ)^2) (2 - (sin θ)^2)+assertEqual "other angles untouched"+  (show ((sin φ)^4 - (cos φ)^4)) "('sin φ)^4 - ('cos φ)^4"
+ test/lib/math/normalize-rules.egi view
@@ -0,0 +1,98 @@+--+-- Normalization-rule behavior that the samples depend on:+-- the rule-suppression quote '( ), negative sqrt powers, and the+-- w rules generated by declare ideal (lib/math/normalize.egi).+--++declare symbol x, θ, θ₂++--+-- The rule-suppression quote '( ): builds the expression with the+-- rule-free structural arithmetic (declare rule rewriting off inside).+--++-- generators survive construction instead of collapsing under auto rules+assertEqual "quoted pythagorean generator"+  (show '((sin θ)^2 + (cos θ)^2 - 1)) "('sin θ)^2 + ('cos θ)^2 - 1"+assertEqual "quoted w generator" (show '(w^2 + w + 1)) "w^2 + w + 1"+-- ... while the unquoted forms are rewritten as usual+assertEqual "plain pythagorean" ((sin θ)^2 + (cos θ)^2 - 1) 0+assertEqual "plain w" (w^2 + w + 1) 0++-- structural normalization still happens inside (merge, expand)+assertEqual "merge inside quote" (show '(2 * x + x)) "3 * x"+assertEqual "expand inside quote" (show '((x + 1)^2)) "x^2 + 2 * x + 1"++-- existing quote meanings are unchanged+assertEqual "function quote" (show ('sqrt x)) "'sqrt x"+assertEqual "operator-section quote" (show ('(^) x 3)) "'^ x 3"+assertEqual "apply simplification untouched" (sqrt 8) (2 * sqrt 2)++--+-- The w rules come from `declare ideal [w^2 + w + 1]`: the single+-- generated rule w^2 -> -1 - w subsumes the old hand-written pair.+--++assertEqual "w^2" (w^2) (-1 - w)+assertEqual "w^3" (w^3) 1+assertEqual "w^5" (w^5) (w^2)+assertEqual "1 + w + w^2" (1 + w + w^2) 0++--+-- Negative powers of sqrt atoms (arising from the Laurent folding of+-- single-term denominators) reduce like positive ones: |n| >= 2+-- splits off the radicand, leaving a remainder exponent in {-1,0,1}.+-- This is what lets thurston.egi's S shed its sqrt factors.+--++def β := `(1 + θ₂ - θ₂^2)+def v := 1 / (sqrt β * x)++assertEqual "sqrt^-2" (show (v * v)) "`(- θ₂^2 + θ₂ + 1)^-1 x^-2"+assertEqual "sqrt^-4" (show (v^2 * v^2)) "`(- θ₂^2 + θ₂ + 1)^-2 x^-4"+assertEqual "sqrt^-3"+  (show (v * v * v))+  "('sqrt `(- θ₂^2 + θ₂ + 1))^-1 * `(- θ₂^2 + θ₂ + 1)^-1 * x^-3"+assertEqual "sqrt^16" ((sqrt β)^16) (β^8)+assertEqual "integer radicand ^-2" ((sqrt 2)^(-2)) (1 / 2)+assertEqual "negative power value preserved" ((v * v * v) * (sqrt β * x)^3) 1++--+-- Depth-2 sqrt denesting (declare apply sqrt, root.egi):+-- sqrt(a + b sqrt c) with a^2 - b^2 c a perfect square opens up.+--++assertEqual "denest 9 - 4 sqrt 5" (sqrt (9 - 4 * sqrt 5)) (sqrt 5 - 2)+assertEqual "denest 7 + 4 sqrt 3" (sqrt (7 + 4 * sqrt 3)) (2 + sqrt 3)+assertEqual "denest rational halves" (sqrt (2 + sqrt 3)) ((sqrt 6 + sqrt 2) / 2)+assertEqual "non-denestable stays"+  (show (sqrt (5 - 2 * sqrt 5))) "'sqrt (-2 * 'sqrt 5 + 5)"+assertEqual "denested square recovers" ((sqrt (9 - 4 * sqrt 5))^2) (9 - 4 * sqrt 5)++--+-- exp structural rules (Math/Rewrite.hs, casRewriteExp): power+-- reduction and product merging; the value rules (exp 0 / exp 1 /+-- exp (n i pi)) stay in the library.+--++assertEqual "exp power" ((exp x)^3) (exp (3 * x))+assertEqual "exp negative power" ((exp x)^(-2)) (exp (-2 * x))+assertEqual "exp merge" (exp x * exp θ) (exp (x + θ))+assertEqual "exp cancel" (exp x * exp (- x)) 1+-- the old declare-rule merge dropped the numeric coefficient+-- (2 y e^x e^y came out as y e^(x+y)); the port fixes it+assertEqual "exp merge keeps the coefficient"+  (show (2 * exp x * θ * exp θ)) "2 * ('exp (θ + x)) * θ"++--+-- Principal-branch normalization of constant radicands+-- (lib/math/common/interval.egi + root.egi): negative constants are+-- certified by interval arithmetic and become i * sqrt(-x).+--++assertEqual "certified sign, positive" (signOfConst (5 - 2 * sqrt 5)) "pos"+assertEqual "certified sign, negative" (signOfConst (-5 + 2 * sqrt 5)) "neg"+assertEqual "negative constant radicand extracts i"+  (show (sqrt (-5 - 2 * sqrt 5))) "('sqrt (2 * 'sqrt 5 + 5)) * i"+assertEqual "principal pair product"+  (sqrt (-5 - 2 * sqrt 5) * sqrt (-5 + 2 * sqrt 5)) (- sqrt 5)
+ test/lib/math/quotient-field.egi view
@@ -0,0 +1,26 @@+-- GF(p^k) as a first-class quotient type: the composition of the+-- coefficient quotient (mod p) and the symbol-carried quotient (the+-- minimal polynomial of α), through finiteFieldReduce+-- (design/cas-simplification.md 3.8, design/type-cas-quotient.md q5).+declare symbol α, x, y++declare cas-quotient GF4 := MathValue by finiteFieldReduce 2 [α^2 + α + 1]++def a : GF4 := projGF4 α+def u : GF4 := projGF4 1++-- the field structure+assertEqual "(α+1)^2 = α" (reprGF4 ((a + u) * (a + u))) α+assertEqual "α (α+1) = 1" (reprGF4 (a * (a + u))) 1+assertEqual "α^3 = 1" (reprGF4 (a * a * a)) 1+assertEqual "characteristic 2" (reprGF4 (a + a)) 0++-- the base is MathValue, so polynomials over GF(4) live in the type:+-- coefficients are disciplined inside every term+assertEqual "coefficients reduced inside polynomials"+  (reprGF4 (projGF4 (x^2 + 3*x + α^2))) (x^2 + x + α + 1)++-- freshman's dream in characteristic 2+assertEqual "(u+v)^2 = u^2 + v^2"+  ((projGF4 (α*x + y)) * (projGF4 (α*x + y)) == projGF4 ((α*x)^2 + y^2))+  True
test/lib/math/tensor.egi view
@@ -1,18 +1,127 @@ ----- This file has been auto-generated by egison-translator.+-- Originally auto-generated by egison-translator; modernized for the+-- typed CAS: free index symbols are introduced with withSymbols (the+-- bare names warned as unbound), and one case that predates the typed+-- syntax is deliberately absent:+--   * "append indices with ..." used a let-bound %-parameter+--     (f %B := B..._j), which no longer parses at let level; the "..."+--     index feature itself is covered by+--     sample/math/geometry/hodge-laplacian-polar.egi.+-- The function-expr case is back below with the canonical positional+-- derivative index (design/function-symbol.md, decided 2026-07-07). -- +declare symbol x, y, z+ assertEqual-  "Tensor product - case 1"-  ([|[|1, 1|], [|0, 1|]|]~i~j . [|[|1, 1|], [|0, 1|]|]_j_k)-  [|[|1, 2|], [|0, 1|]|]+  "bare tensor signature keeps anonymous order"+  (tensorSignature [|1, 2|], dfOrder [|1, 2|])+  (([2], []), 1) +withSymbols [i]+  (assertEqual+    "tensor signature exposes a subscript"+    (tensorSignature [|1, 2|]_i, dfOrder [|1, 2|]_i)+    (([2], [SubIndex i]), 0))++withSymbols [i]+  (assertEqual+    "tensor signature exposes a superscript"+    (tensorSignature [|1, 2|]~i, dfOrder [|1, 2|]~i)+    (([2], [SupIndex i]), 0))++withSymbols [i, j]+  (assertEqual+    "tensor variance names are available to downstream runtimes"+    (tensorVariances [| [|1, 2|], [|3, 4|] |]~i_j)+    ["up", "down"])++withSymbols [i]+  (assertEqual+    "tensor signature exposes a diagonal product index"+    (tensorSignature ([|1, 2|]~i * [|3, 4|]_i),+     dfOrder ([|1, 2|]~i * [|3, 4|]_i))+    (([2], [DiagIndex i]), 0))++def traceWithDynamicRefs X :=+  withSymbols [i]+    sum (contract (subrefs (suprefs X [i]) [i]))+ assertEqual-  "Tensor product - case 2"-  ([|[|1, 1|], [|0, 1|]|]~i~j . [|[|1, 1|], [|0, 1|]|]_j~k . [|[|1, 1|]-  , [|0, 1|]|]_k_l)-  [|[|1, 3|], [|0, 1|]|]~i_l+  "dynamic tensor refs preserve indices on a function parameter"+  (traceWithDynamicRefs [| [|11, 12|], [|21, 22|] |])+  33 +def matrixOperatorInput : Matrix MathValue :=+  [| [|1, 2|], [|3, 4|] |]++assertEqual "matrix trace" (trace matrixOperatorInput) 5++assertEqual+  "symmetric matrix part"+  (sym matrixOperatorInput)+  [| [|1, 5 / 2|], [|5 / 2, 4|] |]++assertEqual+  "antisymmetric matrix part"+  (antisym matrixOperatorInput)+  [| [|0, -1 / 2|], [|1 / 2, 0|] |]++assertEqual+  "symmetric and antisymmetric parts reconstruct a matrix"+  (sym matrixOperatorInput + antisym matrixOperatorInput)+  matrixOperatorInput++withSymbols [i, j]+  (assertEqual+    "matrix operators preserve all ordinary variance sequences"+    (map+      (\A ->+        (tensorIndices (sym A), dfOrder (sym A),+         tensorIndices (antisym A), dfOrder (antisym A)))+      [matrixOperatorInput_i_j, matrixOperatorInput_i~j,+       matrixOperatorInput~i_j, matrixOperatorInput~i~j])+    [([SubIndex i, SubIndex j], 0, [SubIndex i, SubIndex j], 0),+     ([SubIndex i, SupIndex j], 0, [SubIndex i, SupIndex j], 0),+     ([SupIndex i, SubIndex j], 0, [SupIndex i, SubIndex j], 0),+     ([SupIndex i, SupIndex j], 0, [SupIndex i, SupIndex j], 0)])++withSymbols [i]+  (assertEqual+    "contractWith accepts an explicit reducer"+    (contractWith (*) matrixOperatorInput~i_i)+    4)++assertEqual+  "named wedge uses the differential-form library definition"+  (wedge [|1, 2|] [|3, 4|])+  [| [|3, 4|], [|6, 8|] |]++withSymbols [i]+  (assertEqual+    "MathValue dot uses the shared contraction kernel"+    ([|1, 2|]~i .' [|3, 4|]_i)+    11)++assertEqual+  "withSymbols removes temporary tensor indices on exit"+  (tensorSignature (withSymbols [i] [|1, 2|]_i),+   dfOrder (withSymbols [i] [|1, 2|]_i))+  (([2], []), 1)++withSymbols [i, j, k]+  (assertEqual+    "Tensor product - case 1"+    ([|[|1, 1|], [|0, 1|]|]~i~j . [|[|1, 1|], [|0, 1|]|]_j_k)+    [|[|1, 2|], [|0, 1|]|])++withSymbols [i, j, k, l]+  (assertEqual+    "Tensor product - case 2"+    ([|[|1, 1|], [|0, 1|]|]~i~j . [|[|1, 1|], [|0, 1|]|]_j~k . [|[|1, 1|]+    , [|0, 1|]|]_k_l)+    [|[|1, 3|], [|0, 1|]|]~i_l)+ assertEqual "Vector *" (V.* [|1, 1, 0|] [|10, 5, 10|]) 15  assertEqual@@ -29,34 +138,72 @@  assertEqual "Tensor '+' - case 2" ([|1, 2, 3|] + 1) [|2, 3, 4|] +withSymbols [i, j]+  (assertEqual+    "Tensor '+' - case 3"+    ([|[|11, 12|], [|21, 22|], [|31, 32|]|]_i_j + [|100, 200, 300|]_i)+    [|[|111, 112|], [|221, 222|], [|331, 332|]|]_i_j)++withSymbols [i, j]+  (assertEqual+    "Tensor '+' - case 4"+    ([|100, 200, 300|]_i + [|[|11, 12|], [|21, 22|], [|31, 32|]|]_i_j)+    [|[|111, 112|], [|221, 222|], [|331, 332|]|]_i_j)++withSymbols [i, j]+  (assertEqual+    "Tensor '+' - case 5"+    ([|[|1, 2, 3|], [|10, 20, 30|]|]_i_j + [|100, 200, 300|]_j)+    [|[|101, 202, 303|], [|110, 220, 330|]|]_i_j)++withSymbols [i, j]+  (assertEqual+    "Tensor '+' - case 6"+    ([|100, 200, 300|]_j + [|[|1, 2, 3|], [|10, 20, 30|]|]_i_j)+    [|[|101, 110|], [|202, 220|], [|303, 330|]|]_j_i)+ assertEqual-  "Tensor '+' - case 3"-  ([|[|11, 12|], [|21, 22|], [|31, 32|]|]_i_j + [|100, 200, 300|]_i)-  [|[|111, 112|], [|221, 222|], [|331, 332|]|]_i_j+  "generate_tensor completes omitted function-symbol indices"+  (let E := generateTensor (\[i] -> function (x, y, z)) [3]+    in show E)+  "[| E_1 x y z, E_2 x y z, E_3 x y z |]"  assertEqual-  "Tensor '+' - case 4"-  ([|100, 200, 300|]_i + [|[|11, 12|], [|21, 22|], [|31, 32|]|]_i_j)-  [|[|111, 112|], [|221, 222|], [|331, 332|]|]_i_j+  "generate_tensor completes all omitted function-symbol indices"+  (let T := generateTensor (\[i, j] -> function (x, y, z)) [2, 3]+    in show T_2_3)+  "T_2_3 x y z"  assertEqual-  "Tensor '+' - case 5"-  ([|[|1, 2, 3|], [|10, 20, 30|]|]_i_j + [|100, 200, 300|]_j)-  [|[|101, 202, 303|], [|110, 220, 330|]|]_i_j+  "generate_tensor preserves explicit variance and completes omitted axes"+  (let H~i := generateTensor (\[i, j] -> function (x, y, z)) [2, 2]+    in show H~2_1)+  "H~2_1 x y z"  assertEqual-  "Tensor '+' - case 6"-  ([|100, 200, 300|]_j + [|[|1, 2, 3|], [|10, 20, 30|]|]_i_j)-  [|[|101, 110|], [|202, 220|], [|303, 330|]|]_j_i+  "nested generate_tensor accumulates function-symbol indices"+  (let A := generateTensor+              (\[i] -> generateTensor (\[j] -> function (x)) [2])+              [2]+    in show A)+  "[| [| A_1_1 x, A_1_2 x |], [| A_2_1 x, A_2_2 x |] |]"  assertEqual-  "append indices with ..."-  (let A := generateTensor (\_ -> 1) [2, 2]-       f %B := B..._j-    in f A_i)-  [|[|1, 1|], [|1, 1|]|]_i_j+  "nested generate_tensor preserves completed explicit indices"+  (let A~i := generateTensor+                (\[i] -> generateTensor (\[j] -> function (x)) [2])+                [2]+    in show (withSymbols [i] A~i))+  "[| [| A~1_1 x, A~1_2 x |], [| A~2_1 x, A~2_2 x |] |]"  assertEqual+  "generate_tensor does not rename referenced function symbols"+  (let f := function (x, y)+    in let R := generateTensor (\_ -> f) [3]+        in show R)+  "[| f x y, f x y, f x y |]"++assertEqual   "generate_tensor by using function expr"   (let g_i_j := (generateTensor                   (\match as list integer with@@ -64,4 +211,4 @@                     | _        -> 0)                   [3, 3])_i_j     in show (withSymbols [i, j] d/d g_i_j x))-  "[| [| g_1_1|x, 0, 0 |], [| 0, g_2_2|x, 0 |], [| 0, 0, g_3_3|x |] |]"+  "[| [| g_1_1|1 x y z, 0, 0 |], [| 0, g_2_2|1 x y z, 0 |], [| 0, 0, g_3_3|1 x y z |] |]"
+ test/primitive.egi view
@@ -0,0 +1,137 @@+assertEqual "numerator" (numerator (13 / 21)) 13++assertEqual "denominator" (denominator (13 / 21)) 21++assertEqual "i.modulo" (i.modulo (-21) 13) 5++assertEqual "i.quotient" (i.quotient (-21) 13) (-1)++assertEqual "i.%" (i.% (-21) 13) (-8)++assertEqual "i.neg" (i.neg (-89)) 89++assertEqual "i.abs" (i.abs 0)     0+assertEqual "i.abs" (i.abs 15)    15+assertEqual "i.abs" (i.abs (-89)) 89++assertEqual "f.<" (f.< 0.1 1.0) True+assertEqual "f.<" (f.< 1.0 0.1) False+assertEqual "f.<" (f.< 1.0 1.0) False++assertEqual "f.<=" (f.<= 0.1 1.0) True+assertEqual "f.<=" (f.<= 1.0 0.1) False+assertEqual "f.<=" (f.<= 1.0 1.0) True++assertEqual "f.>" (f.> 0.1 1.0) False+assertEqual "f.>" (f.> 1.0 0.1) True+assertEqual "f.>" (f.> 1.0 1.0) False++assertEqual "f.>=" (f.>= 0.1 1.0) False+assertEqual "f.>=" (f.>= 1.0 0.1) True+assertEqual "f.>=" (f.>= 1.0 1.0) True++assertEqual "round" (round 3.1)              3+assertEqual "round" (round 3.7)              4+assertEqual "round" (round (f.- 0.0 2.2))    (-2)+assertEqual "round" (round (f.- 0.0 2.7))    (-3)++assertEqual "floor" (floor 3.1)              3+assertEqual "floor" (floor 3.7)              3+assertEqual "floor" (floor (f.- 0.0 2.2))    (-3)+assertEqual "floor" (floor (f.- 0.0 2.7))    (-3)++assertEqual "ceiling" (ceiling 3.1)           4+assertEqual "ceiling" (ceiling 3.7)           4+assertEqual "ceiling" (ceiling (f.- 0.0 2.2)) (-2)+assertEqual "ceiling" (ceiling (f.- 0.0 2.7)) (-2)++assertEqual "truncate" (truncate 3.1)              3+assertEqual "truncate" (truncate 3.7)              3+assertEqual "truncate" (truncate (f.- 0.0 2.2))    (-2)+assertEqual "truncate" (truncate (f.- 0.0 2.7))    (-2)++assertEqual "f.sqrt" (f.sqrt 4.0) 2.0+assertEqual "f.sqrt" (f.sqrt 1.0) 1.0++assertEqual "f.exp" (f.exp 0.0) 1.0+assertEqual "f.exp" (f.exp 1.0) 2.718281828459045++assertEqual "f.log" (f.log 1.0) 0.0+assertEqual "f.log" (f.log 10.0) 2.302585092994046++assertEqual "f.sin"   (f.sin 0.0) 0.0+assertEqual "f.cos"   (f.cos 0.0) 1.0+assertEqual "f.tan"   (f.tan 0.0) 0.0+assertEqual "f.asin"  (f.asin 0.0) 0.0+assertEqual "f.acos"  (f.acos 1.0) 0.0+assertEqual "f.atan"  (f.atan 0.0) 0.0+assertEqual "f.sinh"  (f.sinh 0.0) 0.0+assertEqual "f.cosh"  (f.cosh 0.0) 1.0+assertEqual "f.tanh"  (f.tanh 0.0) 0.0+assertEqual "f.asinh" (f.asinh 0.0) 0.0+assertEqual "f.acosh" (f.acosh 1.0) 0.0+assertEqual "f.atanh" (f.atanh 0.0) 0.0++-- tensorSize+-- tensorToList+-- dfOrder++assertEqual "itof" (itof 4)  4.0+assertEqual "itof" (itof (i.neg 1)) (f.- 0.0 1.0)++assertEqual "rtof" (rtof (3 / 2)) 1.5+assertEqual "rtof" (rtof 1)       1.0++assertEqual "ctoi" (ctoi '1') 49++assertEqual "itoc" (itoc 49) '1'++assertEqual "pack" (pack []) ""+assertEqual "pack" (pack ['E', 'g', 'i', 's', 'o', 'n']) "Egison"++assertEqual "unpack" (unpack "Egison") ['E', 'g', 'i', 's', 'o', 'n']+assertEqual "unpack" (unpack "") []++assertEqual "unconsString" (unconsString "Egison") ('E', "gison")++assertEqual "lengthString" (lengthString "") 0+assertEqual "lengthString" (lengthString "Egison") 6++assertEqual "appendString" (appendString "" "")       ""+assertEqual "appendString" (appendString "" "Egison") "Egison"+assertEqual "appendString" (appendString "Egison" "") "Egison"+assertEqual "appendString" (appendString "Egi" "son") "Egison"++assertEqual "splitString" (splitString "," "") [""]+assertEqual "splitString" (splitString "," "2,3,5,7,11,13") ["2", "3", "5", "7", "11", "13"]++assertEqual "regex" (regex "cde" "abcdefg") [("ab", "cde", "fg")]+assertEqual "regex" (regex "[0-9]+" "abc123defg") [("abc", "123", "defg")]+assertEqual "regex" (regex "a*" "") [("", "", "")]++assertEqual "regexCg" (regexCg "([0-9]+),([0-9]+)" "abc,123,45,defg") [("abc,", ["123", "45"], ",defg")]++-- addSubscript+-- addSuperscript++-- TODO: read+-- assertEqual "read" (read "3")                3+-- assertEqual "read" (read "3.14")             3.14+-- assertEqual "read" (read "[1, 2]")            [1, 2]+-- assertEqual "read" (read "\"Hello world!\"") "Hello world!"++-- TODO: read-tsv++assertEqual "show" (show 3)              "3"+assertEqual "show" (show 3.14159)        "3.14159"+assertEqual "show" (show [1, 2])         "[1, 2]"+assertEqual "show" (show "Hello world!") "\"Hello world!\""++-- TODO: show-tsv++assertEqual "isInteger" (isInteger 1) True+assertEqual "isInteger" (isInteger (1 / 2)) False++assertEqual "isRational" (isRational 1)       True+assertEqual "isRational" (isRational (1 / 2)) True+assertEqual "isRational" (isRational 1.0)     False
+ test/syntax.egi view
@@ -0,0 +1,529 @@+--+-- Syntax test+--++--+-- Primitive Data+--++assertEqual "char literal"+  ['a', '\n', '\'']+  ['a', '\n', '\'']++assertEqual "string literal" "" ""+assertEqual "string literal" "abc\n" "abc\n"++assertEqual "bool literal"+  [True, False]+  [True, False]++assertEqual "integer literal"+  [1, 0, -100, 1 - 100]+  [1, 0, -100, -99]++assertEqual "rational number"+  [10 / 3, 10 / 20, -1 / 2]+  [10 / 3 , 1 / 2, -1 / 2]++assertEqual "float literal" [1.0, 0.0, f.- 0.0 100.012001, f.+ 1.0 2.0] [1.0, 0.0, f.- 0.0 100.012001, 3.0]++assertEqual "tuple literal" (1, 2, 3) (1, 2, 3)++assertEqual "collection literal" [1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6]++assertEqual "collection between" [1..5] [1, 2, 3, 4, 5]+assertEqual "collection from" (take 5 [1..]) [1, 2, 3, 4, 5]++assertEqual "identifier with dot and operator" (i.* 1 2) 2++--+-- Basic Sytax+--++assertEqual "if"+  (if True then True else False)+  True++assertEqual "if"+  (if False then True else False)+  False++assertEqual "let binding"+  (let t := (1, 2)+       (x, y) := t+    in x + y)+  3++assertEqual "let binding"+  (let x := 1+       y := x + 1+    in y)+  2++assertEqual "let binding without newline"+  (let { x := 1; y := x + 1 } in y)+  2++io $ do print "io and do expression"+        return 0++io $ do { print "io and do expression without newline"; return 0 }++assertEqual "where"+  (f 0 + y + 1+    where f x := 2 + x+          y := 3)+  6++assertEqual "nested where"+  (f 0 + 1+    where+      f x := 2 + y + z+        where y := 3+      z := 4)+  10++assertEqual "multiple where in one expression"+  (matchAll [1, 2, 3] as multiset integer with+   | #1 :: $xs -> f xs+     where f xs := length xs+   | #2 :: #3 :: $xs -> g xs+     where g xs := length xs)+  [2, 1]++assertEqual "mutual recursion"+  (let isEven n := if n = 0 then True else isOdd (n - 1)+       isOdd  n := if n = 0 then False else isEven (n - 1)+    in isEven 10)+  True++assertEqual "lambda and application"+  ((\x -> x + 1) 10)+  11++assertEqual "application with binops"+  ((\x y -> x + y) 1 2 + 3)+  6++assertEqual "lambda with case"+  ((\() -> 1) ())+  1++assertEqual "lambda with case"+  ((\(x, y, z) -> x - y - z) (1, 2, 3))+  (-4)++assertEqual "lambda with case"+  ((\_ -> 1) 2)+  1++assertEqual "append op" ([1] ++ [2]) [1, 2]+assertEqual "append op" ((++) [1] [2]) [1, 2]++assertEqual "apply op" ((+ 5) $ 1 + 2) 8++assertEqual "section" ((+) 10 1) 11+assertEqual "section" ((+ 1) 10) 11+assertEqual "section" (foldl (*) 1 [1..5]) 120+assertEqual "section" ((-) 10 1) 9+assertEqual "section" ((10 -) 1) 9+assertEqual "section" ((10 - ) 1) 9+assertEqual "section" ((-1 +) 2) 1+assertEqual "safe section - left assoc"  ((1 + 2 +) 3) 6+assertEqual "safe section - right assoc" ((++ [1] ++ [2]) [3]) [3, 1, 2]+assertEqual "not section" (- 2) (1 - 3)++-- user-defined infix+infixl expression 5 @+def (@) x y := x - y++assertEqual "user defined infix"+  (4 @ 3 @ 5)+  (-4)++def findFactor :=+  memoizedLambda n ->+    match takeWhile (<= floor (f.sqrt (itof n))) primes as list integer with+    | _ ++ (?(\m -> divisor n m) & $x) :: _ -> x+    | _ -> n++assertEqual "memoized lambda"+  (map findFactor [1..10])+  [1, 2, 3, 2, 5, 2, 7, 2, 3, 2]++def twinPrimes :=+  matchAll primes as list integer with+  | _ ++ $p :: #(p + 2) :: _ -> (p, p + 2)++assertEqual "twin primes"+  (take 10 twinPrimes)+  [(3, 5), (5, 7), (11, 13), (17, 19), (29, 31), (41, 43), (59, 61), (71, 73), (101, 103), (107, 109)]++def primeTriplets :=+  matchAll primes as list integer with+  | _ ++ $p :: ((#(p + 2) | #(p + 4)) & $m) :: #(p + 6) ::  _+  -> (p, m, p + 6)++assertEqual "prime triplets"+  (take 10 primeTriplets)+  [(5, 7, 11), (7, 11, 13), (11, 13, 17), (13, 17, 19), (17, 19, 23), (37, 41, 43), (41, 43, 47), (67, 71, 73), (97, 101, 103), (101, 103, 107)]++def someFunction x y z :=+  x + y * z++assertEqual "function definition"+  (someFunction 1 2 3)+  7++-- (named to avoid shadowing GCDDomain's method `gcd`, which warns;+-- unannotated, on purpose: the definition's own name is bound to a+-- monomorphic placeholder during body inference, so top-level+-- recursion needs no signature)+def euclid m n :=+  if m >= n then+            if n = 0 then m+                     else euclid n (m % n)+            else euclid n m++assertEqual "recursive function definition"+  (euclid 143 22)+  11++def A x := 1++assertEqual "definition of upper-case identifier"+  (A 2)+  1++def f0 () := 1+def f2 (x, y) := x + y++assertEqual "nullary function definition"+  (f0 ())+  1++assertEqual "function definition with tupled argument"+  (f2 (1, 2))+  3++{-+  This is a comment+ -}++{-+  {- We can nest comments! -}+  {- {- nested -} comment -}+ -}++--+-- Pattern-Matching+--++assertEqual "match"+  (match 1 as integer with+   | #0 -> 0+   | $x -> 10 + x)+  11++assertEqual "match-all"+  (matchAll [1, 2, 3] as multiset integer with+   | $x :: _ -> x)+  [1, 2, 3]++assertEqual "match-all-multi"+  (matchAll [1, 2, 3] as multiset integer with+   | $x :: #(x + 1) :: _ -> [x, x + 1]+   | $x :: #(x + 2) :: _ -> [x, x + 2])+  [[1, 2], [2, 3], [1, 3]]++assertEqual "match-lambda"+  ((\match as list integer with+    | [] -> 0+    | $x :: _ -> x) [1, 2, 3])+  1++assertEqual "match-all-lambda"+  ((\matchAll as list something with+    | _ ++ $x :: _ -> x) [1, 2, 3])+  [1, 2, 3]++-- Uses `multiset integer` (not `multiset something`): the value patterns `#(x + 1)` /+-- `#(x + 2)` give the element a concrete (Integer) structural type, at which a bare-variable+-- matcher (`something`) is not structurally admissible under the MatcherSlot type system+-- (paper's per-use-site structural admissibility); a concrete element matcher is required.+assertEqual "match-all-lambda-multi"+  ((\matchAll as multiset integer with+    | $x :: #(x + 1) :: _ -> [x, x + 1]+    | $x :: #(x + 2) :: _ -> [x, x + 2]) [1, 2, 3])+  [[1, 2], [2, 3], [1, 3]]++assert "nested pattern match"+  (match [1, 2, 3] as list integer with+   | #2 :: $x -> match x as multiset integer with+                | _ -> False+   | #1 :: $x -> match x as multiset integer with+                | #1 :: _ -> False+                | #2 :: _ -> True)++assertEqual "pattern variable"+  (match 1 as something with $x -> x)+  1++assert "value pattern" (match 1 as integer with #1 -> True)++assert "inductive pattern"+  (match [1, 2, 3] as list integer with+   | _ *: #3 -> True)++assert "collection pattern - nil"+  (match [] as list integer with+   | [] -> True)++assertEqual "collection pattern"+  (match [1, 2, 3] as list integer with+   | [#1, _, $x] -> x)+  3++assertEqual "collection pattern"+  (matchAll [1, 2, 3, 4] as list integer with+   | [_, _, _] -> True)+  []++assert "and pattern"+  (match [1, 2, 3] as list integer with+   | #1 :: _ & _ *: #3 -> True)++assert "and pattern"+  (match [1, 2, 3] as list integer with+   | #1 :: _ & #3 :: _ -> False+   | _ -> True)++assert "or pattern"+  (match [1, 2, 3] as list integer with+   | _ *: #1 | _ *: #3 -> True)++assert "or pattern"+  (match [1, 2, 3] as list integer with+   | #2 :: _ | #1 :: _ -> True)++assert "not pattern"+  (match [1, 2] as list integer with+   | _ *: !#1 -> True+   | !#1 :: _ -> False)++assertEqual "not pattern"+  (matchAll [1, 2, 2, 3, 3, 3] as multiset integer with+   | $n :: !(#n :: _) -> n)+  [1]++assert "predicate pattern"+  (match [1, 2, 3] as list integer with+   | ?(= 1) :: _ -> True)++assert "predicate pattern"+  (match [1, 2, 3] as list integer with+   | ?(= 2) :: _ -> False+   | _ -> True)++assertEqual "indexed pattern variable"+  (match 23 as mod 10 with+   | $a_1 -> a)+  {| (1, 23) |}++--assert "loop pattern"+--  (match [3, 2, 1] as list integer with+--   | loop $i (1, [3], _)+--       (... *: #i)+--       [] -> True)++assertEqual "loop pattern"+  (match [1..10] as list integer with+   | loop $i (1, $n)+       (#i :: ...)+       [] -> n)+  10++assertEqual "let pattern"+  (match [1, 2, 3] as list integer with+   | let a := 42 in _ -> a)+  42++assertEqual "tuple pattern"+  (matchAll (1, (2, 3)) as (integer, (integer, integer)) with+   | ($m, ($n, $w)) -> [m, n, w])+  [[1, 2, 3]]++assertEqual "tuple pattern"+  (matchAll [(1, 1), (2, 2)] as multiset (integer, integer) with+   | ($x, #x) :: _ -> x)+  [1, 2]++assertEqual "pairs of 2, natural numbers"+  (take 10 (matchAll nats as set integer with+            | $m :: $n :: _ -> [m, n]))+  [[1, 1], [1, 2], [2, 1], [1, 3], [2, 2], [3, 1], [1, 4], [2, 3], [3, 2], [4, 1]]++assertEqual "pairs of 2, different natural numbers"+  (take 10 (matchAll nats as list integer with+            | _ ++ $m :: _ ++ $n :: _ -> [m, n]))+  [[1, 2], [1, 3], [2, 3], [1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [4, 5]]++assertEqual "combinations"+  (matchAll [1,2,3] as list something with+   | _ ++ $x :: _ ++ $y :: _ -> (x, y))+  [(1, 2), (1, 3), (2, 3)]++assertEqual "permutations"+  (matchAll [1,2,3] as multiset something with+   | $x :: $y :: _ -> (x, y))+  [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]++assertEqual "sequential not pattern"+  (matchAll ([1,2,3], [4,3,5]) as (multiset eq, multiset eq) with+   | { ($x :: @, #x :: @),+       !($y :: _, #y :: _) }+   -> x)+  [3]++assertEqual "partial sequential pattern"+  (matchAll ([1,2,3,2], [10,20]) as (list eq, list eq) with+   | ({ @ ++ $x :: _, !(_ ++ #x :: _) }, $ys) -> (x, ys))+  [(1, [10, 20]), (2, [10, 20]), (3, [10, 20])]++assertEqual "forall pattern 1"+  (matchAll [1,5,3] as multiset integer with+   | forall _ _ -> "ok")+  ["ok"]++--+-- Tensor+--++assertEqual "generate-tensor"+  (generateTensor product [3, 5])+  [| [| 1, 2, 3, 4, 5 |], [| 2, 4, 6, 8, 10 |], [| 3, 6, 9, 12, 15 |] |]++assertEqual "generate vector using generate-tensor"+  (generateTensor (\[x] -> x + 1) [3])+  [| 2, 3, 4 |]++assertEqual "generate scalar using rank-zero generate-tensor"+  (generateTensor (\[] -> 42) [])+  42++assertEqual "rank-one singleton generated value remains a tensor"+  (generateTensor (\[x] -> x) [1])+  [| 1 |]++assertEqual "empty rank-one generated value remains a tensor"+  (show (generateTensor (\_ -> 42) [0]))+  "[|  |]"++assertEqual "tensor"+  (tensor [2, 5] [1, 2, 3, 4, 5, 2, 4, 6, 8, 10])+  [| [| 1, 2, 3, 4, 5 |], [| 2, 4, 6, 8, 10 |] |]++assertEqual "tensor wedge expr"+  (! min [| 1, 2, 3 |] [| 1, 2, 3 |])+  [| [| 1, 1, 1 |], [| 1, 2, 2 |], [| 1, 2, 3 |] |]++assertEqual "tensor wedge expr of binary operator"+  ([| 1, 2, 3 |] !+ [| 1, 2, 3 |])+  [| [| 2, 3, 4 |], [| 3, 4, 5 |], [| 4, 5, 6 |] |]++assertEqual "tensor wedge expr of binary operator - section style"+  ((!+) [| 1, 2, 3 |] [| 1, 2, 3 |])+  [| [| 2, 3, 4 |], [| 3, 4, 5 |], [| 4, 5, 6 |] |]++assertEqual "tensor multiplication"+  ([| 1, 2, 3 |]_i * [| 1, 2, 3 |]_i)+  [| 1, 4, 9 |]_i++assertEqual "multi subscript"+  (let i := {| (1, 1), (2, 2), (3, 3) |}+       x := generateTensor sum [5, 5, 5]+    in x_(i_1)..._(i_3))+  6++declare symbol x, a, b, c: MathExpr++def TestT := generateTensor (\[a, b, c] -> x_a_b_c) [2,3,4]+def TestC_c_a_b := TestT_a_b_c++assertEqual "transpose"+  TestC_#_#_#+  (tensor [4, 2, 3]+   [x_1_1_1, x_1_2_1, x_1_3_1, x_2_1_1, x_2_2_1, x_2_3_1,+    x_1_1_2, x_1_2_2, x_1_3_2, x_2_1_2, x_2_2_2, x_2_3_2,+    x_1_1_3, x_1_2_3, x_1_3_3, x_2_1_3, x_2_2_3, x_2_3_3,+    x_1_1_4, x_1_2_4, x_1_3_4, x_2_1_4, x_2_2_4, x_2_3_4])_#_#_#++def symmT[_i_j] :=+  [| [| 0, 1, 2 |],+     [| 1, 0, 3 |],+     [| 2, 3, 0 |] |]++def asymmT{_i_j} :=+  [| [| 0, 1, 2 |],+     [| -1, 0, 3 |],+     [| -2, -3, 0 |] |]++assert "symmetric tensor"+  (symmT_1_1 = 0 && symmT_1_2 = 1 && symmT_1_3 = 2 &&+   symmT_2_1 = 1 && symmT_2_2 = 0 && symmT_2_3 = 3 &&+   symmT_3_1 = 2 && symmT_3_2 = 3 && symmT_3_3 = 0)++assert "symmetric tensor"+  (asymmT_1_1 = 0  && asymmT_1_2 = 1  && asymmT_1_3 = 2 &&+   asymmT_2_1 = -1 && asymmT_2_2 = 0  && asymmT_2_3 = 3 &&+   asymmT_3_1 = -2 && asymmT_3_2 = -3 && asymmT_3_3 = 0)++--+-- Hash+--++assertEqual "hash-literal"+  {| (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), |}+  {| (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), |}++assertEqual "empty hash-literal"+  {| |}+  {| |}++assertEqual "hash access"+  {| (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), |}_3+  13++assertEqual "string hash access"+  {| ("1", 11), ("2", 12), ("3", 13), ("4", 14), ("5", 15) |}_"3"+  13++assertEqual "char hash access"+  {| ('a', 11), ('b', 12), ('c', 13), ('d', 14), ('e', 15) |}_'c'+  13++-- Primitive data pattern match with let expression+assertEqual "let pattern match"+  (let (x :: xs) := [1, 2, 3] in (x, xs))+  (1, [2, 3])++assertEqual "let pattern match"+  (let (xs *: x) := [1, 2, 3] in (xs, x))+  ([1, 2], 3)++assertEqual "let pattern match"+  (let (x, y) := (2, 3) in x + y)+  5++-- Functions to become binary operator (of infixl 7) when surrounded with 2 backquotes+assertEqual "function to become binary operator"+  (10 `i.modulo` 4 + 5)+  7++assertEqual "function to become binary operator with space"+  (10` i.modulo `4)+  2++assertEqual "function to become binary operator with section"+  ((`i.modulo` 4) 10)+  2
+ test/type-error/01-something-cons.egi view
@@ -0,0 +1,9 @@+--+-- Match-site dual check, direct supply (paper Appendix B, Case 2).+-- `something : Matcher b` cannot fill the cons pattern's slot+-- MatcherSlot [a'] [Integer]: the structural check b' <: [a'] fails.+--+-- Expected: Type error (Matcher b vs MatcherSlot [a'] [Integer])+--++def t := matchAll [1, 2, 3] as something with $x :: $xs -> (x, xs)
+ test/type-error/02-something-cons-param.egi view
@@ -0,0 +1,12 @@+--+-- Match-site dual check through a function parameter (paper Appendix B, Case 3).+-- The cons pattern commits `m` to MatcherSlot [a'] [Integer]; supplying+-- `something` at the application site fails the structural check there.+--+-- Expected: Type error at `f something` (structural check fails at the+-- application site)+--++def f m := matchAll [1, 2, 3] as m with $x :: $xs -> (x, xs)++def t := f something
+ test/type-error/03-nested-ctor-element.egi view
@@ -0,0 +1,13 @@+--+-- Nested constructor sub-pattern (paper Appendix B, "Nested constructor+-- sub-patterns").  The element position of the cons is pinned to Tile by+-- `num`, so the structural index is [Tile]; `multiset something` fails+-- [b'] <: [Tile].+--+-- Expected: Type error (multiset something vs MatcherSlot [Tile] ...)+--++inductive Tile := Num Integer | Hnr Integer+inductive pattern Tile := num Integer | hnr Integer++def t := matchAll [Num 1, Num 2] as multiset something with num $n :: $rest -> n
+ test/type-error/04-something-tuple-pattern.egi view
@@ -0,0 +1,9 @@+--+-- Tuple pattern at a bare-variable matcher (paper Lemma "MS Progress",+-- tuple case).  The tuple pattern's structural index is product-headed,+-- so `something : Matcher b` fails the structural check b' <: (a' x b').+--+-- Expected: Type error (something vs a product-headed slot)+--++def t := matchAll (1, 2) as something with ($x, $y) -> x
+ test/type-error/05-matcher-target-mismatch.egi view
@@ -0,0 +1,11 @@+--+-- Matcher-target mismatch (paper Appendix B.2.1).  The pattern constructor+-- `num` belongs to Tile, but the matcher and target are [Integer].+--+-- Expected: Type error (Tile vs Integer)+--++inductive Tile := Num Integer | Hnr Integer+inductive pattern Tile := num Integer | hnr Integer++def t := matchAll [1, 2, 3] as multiset integer with num $n :: _ -> n
+ test/type-error/06-target-type-mismatch.egi view
@@ -0,0 +1,8 @@+--+-- Matcher vs target-expression type mismatch (T-MATCHALL's target side).+-- `multiset integer : Matcher [Integer]` cannot consume the target `5 : Integer`.+--+-- Expected: Type error ([Integer] vs Integer)+--++def t := matchAll 5 as multiset integer with $x -> x
+ test/type-error/10-patfun-body-structural.egi view
@@ -0,0 +1,12 @@+--+-- Pattern function structural propagation, body side (paper PAT-APP;+-- review counterexample M1).  `pair`'s body is cons-headed, so the+-- application's structural index is [b']; `something` fails the check.+-- Before the PAT-APP fix this was well-typed and stuck at runtime.+--+-- Expected: Type error (Matcher a vs MatcherSlot [taup] [Integer])+--++def pattern pair {a} (pat1: a) (pat2: [a]) : [a] := ($pat & ~pat1) :: #pat :: ~pat2++def t := matchAll [1, 1] as something with pair $x [] -> x
+ test/type-error/11-patfun-arg-structural.egi view
@@ -0,0 +1,12 @@+--+-- Pattern function structural propagation, argument side (paper PAT-APP;+-- review counterexample M1).  `idp`'s body is just its parameter, so the+-- argument's cons-headed structural index flows through to the+-- application; `something` fails the check.+--+-- Expected: Type error (Matcher a vs MatcherSlot [taup] [Integer])+--++def pattern idp {a} (p: [a]) : [a] := ~p++def t := matchAll [1, 2] as something with idp ($x :: $xs) -> x
+ test/type-error/12-patfun-nested-arg-structural.egi view
@@ -0,0 +1,16 @@+--+-- Pattern function structural propagation into a nested argument (paper+-- Appendix B, "Propagation through pattern functions").  Supplying+-- `num $n` for pair's first parameter drives Tile into the element+-- position of the recorded scheme, so the application's structural index+-- is [Tile] and `multiset something` fails [b'] <: [Tile].+--+-- Expected: Type error (multiset something vs MatcherSlot [Tile] [Tile])+--++inductive Tile := Num Integer | Hnr Integer+inductive pattern Tile := num Integer | hnr Integer++def pattern pair {a} (pat1: a) (pat2: [a]) : [a] := ($pat & ~pat1) :: #pat :: ~pat2++def t := matchAll [Num 1, Num 1] as multiset something with pair (num $n) [] -> n
+ test/type-error/13-patfun-target.egi view
@@ -0,0 +1,15 @@+--+-- Pattern function target side (PAT-APP's target half).  `seqp` produces+-- a Pattern [Tile], but the matcher and target are [Integer].  Before the+-- PAT-APP fix this silently failed at runtime.+--+-- Expected: Type error (Tile vs Integer)+--++inductive Tile := Num Integer | Hnr Integer+inductive pattern Tile := num Integer | hnr Integer++def pattern seqp (pat1: Tile) (pat2: [Tile]) : [Tile] :=+  (num $n & ~pat1) :: num #(n + 1) :: ~pat2++def t := matchAll [1, 2] as multiset integer with seqp #(Num 1) _ -> True
+ test/type-error/14-patfun-arg-target.egi view
@@ -0,0 +1,15 @@+--+-- Pattern function argument target type (paper Appendix B.1.2).  `seqp`+-- expects Pattern Tile as its first argument, but `#1` has type+-- Pattern Integer.  Before the PAT-APP fix this silently failed at runtime.+--+-- Expected: Type error (Integer vs Tile)+--++inductive Tile := Num Integer | Hnr Integer+inductive pattern Tile := num Integer | hnr Integer++def pattern seqp (pat1: Tile) (pat2: [Tile]) : [Tile] :=+  (num $n & ~pat1) :: num #(n + 1) :: ~pat2++def t := matchAll [Num 1, Num 2] as multiset something with seqp #1 _ -> True
+ test/type-error/20-patfun-linearity-unused.egi view
@@ -0,0 +1,10 @@+--+-- Pattern function linearity, unused parameter (PATFUN-DEF side condition;+-- review M2(i)).  `p2` never occurs in the body, so an application+-- `unused $x $y` would promise a binding for y that matching never+-- produces.+--+-- Expected: Type error (parameter linearity: uses ~p1 only)+--++def pattern unused {a} (p1: a) (p2: a) : a := ~p1
+ test/type-error/21-patfun-linearity-order.egi view
@@ -0,0 +1,10 @@+--+-- Pattern function linearity, out-of-order parameters (PATFUN-DEF side+-- condition; review M2(iii)).  The body uses ~p2 before ~p1, so at+-- `flipped $x #x` the value pattern #x would be evaluated before $x+-- binds x.+--+-- Expected: Type error (parameter linearity: uses ~p2, ~p1)+--++def pattern flipped {a} (p1: a) (p2: a) : (a, a) := (~p2, ~p1)
+ test/type-error/22-patfun-linearity-dup.egi view
@@ -0,0 +1,9 @@+--+-- Pattern function linearity, duplicated parameter (PATFUN-DEF side+-- condition; review M2(ii)).  `p` occurs twice, so a single argument+-- pattern would be expanded twice along one matching path.+--+-- Expected: Type error (parameter linearity: uses ~p, ~p)+--++def pattern twice {a} (p: a) : [a] := ~p :: ~p :: _
+ test/type-error/23-patfun-linearity-under-or.egi view
@@ -0,0 +1,9 @@+--+-- Pattern function parameter under an or-alternative (PATFUN-DEF side+-- condition).  Along the right alternative the argument is never+-- expanded, so its bindings would be missing.+--+-- Expected: Type error (parameter used under a branching pattern)+--++def pattern orp {a} (p: a) : a := ~p | _
+ test/type-error/30-value-pattern-expr-type.egi view
@@ -0,0 +1,9 @@+--+-- Ill-typed value pattern (paper Appendix B.1.1).  The value pattern's+-- expression is typed under the bindings of the preceding sub-patterns:+-- x : Integer, so `x ++ [1]` is ill-typed.+--+-- Expected: Type error ((++) expects a list, x is Integer)+--++def t := matchAll [1, 2, 3] as multiset integer with $x :: #(x ++ [1]) :: _ -> x
+ test/type-error/31-nonlinear-target-type.egi view
@@ -0,0 +1,9 @@+--+-- Non-linear pattern type mismatch (paper Appendix B.1.1).  `#x` has type+-- Pattern Integer (x is bound by $x), but the tail of `::` requires+-- Pattern [Integer].  The correct pattern is `$x :: #x :: _`.+--+-- Expected: Type error (Integer vs [Integer])+--++def t := matchAll [1, 2, 3] as multiset integer with $x :: #x -> x
+ test/type-error/32-or-pattern-bindings.egi view
@@ -0,0 +1,8 @@+--+-- Or-pattern with different bindings in the branches (PAT-OR requires the+-- same output context).+--+-- Expected: Type error (branches bind {x} vs {y})+--++def t := matchAll [1, 2, 3] as multiset integer with ($x :: _) | (_ :: $y :: _) -> 0
+ test/type-error/40-matcher-next-structural.egi view
@@ -0,0 +1,20 @@+--+-- Structurally inadmissible next matcher (paper Appendix B, the `weird`+-- matcher; Definition 4.2(1a) / PP-Con).  The tail hole of the cons+-- clause has structural index [a''], which `something` cannot fill; the+-- runtime counterpart would route a cons sub-pattern to something and+-- get stuck.+--+-- Expected: Type error (something at the list-headed tail hole)+--++def weird {a} (m: MatcherSlot a a) : Matcher [a] :=+  matcher+    | [] as () with+      | [] -> [()]+      | _ -> []+    | $ :: $ as (m, something) with+      | $x :: $xs -> [(x, xs)]+      | _ -> []+    | $ as something with+      | $tgt -> [tgt]
+ test/type-error/41-matcher-body-matchsite.egi view
@@ -0,0 +1,14 @@+--+-- Match site nested inside a matcher body is genuinely checked: `integer`+-- cannot fill the cons pattern's slot in the inner match.+--+-- Expected: Type error (integer vs a cons-headed slot in the inner match)+--++def badInner : Matcher [Integer] :=+  matcher+    | $ as something with+      | $tgt ->+        match tgt as integer with+          | $x :: $xs -> [tgt]+          | _ -> [tgt]
+ test/type-error/42-tuple-pattern-arity.egi view
@@ -0,0 +1,8 @@+--+-- Tuple pattern arity mismatch: a 3-tuple pattern against a pair target+-- and a pair product matcher.+--+-- Expected: Type error (tuple pattern vs (Integer, Integer))+--++def t := matchAll (1, 2) as (integer, integer) with ($x, $y, $z) -> x
+ test/type-error/50-matcher-collection-hetero.egi view
@@ -0,0 +1,16 @@+--+-- Heterogeneous matcher collection (matcher rigidity).  Putting `something :+-- Matcher b` and `list integer : Matcher [Integer]` in one list would unify+-- b with [Integer], giving `something` a matcher type whose structural+-- capability it does not have: binding $m to the first element and using it+-- at a cons pattern would then pass the dual check yet get stuck at runtime.+-- Matcher types are rigid -- their unification is forbidden -- so the list+-- literal itself is rejected.+--+-- Expected: Type error (matcher types are rigid: Matcher b vs Matcher [Integer])+--++def t := matchAll [something, list integer] as list something with+  | $m :: _ ->+    matchAll [1, 2] as m with+      | $x :: _ -> x
+ test/type-error/51-matcher-cast-structured.egi view
@@ -0,0 +1,5 @@+-- Matcher rigidity: a bare-variable matcher value cannot be bound at a+-- structured matcher type.  This cast would let cons patterns reach+-- `something`, which cannot decompose them (well-typed but stuck).++def m2 : Matcher [Integer] := something
+ test/type-error/52-missing-signature-constraint.egi view
@@ -0,0 +1,8 @@+-- A definition whose body requires a type-class constraint that its+-- signature does not declare must be rejected (the signature is the+-- contract; if the body needs {Ord a}, the signature must say so).+--+-- Here <= is an Ord method used at the signature's type variable `a`,+-- but the signature declares no {Ord a}.++def needsOrd {a} (x: a) (y: a) : Bool := x <= y
+ test/type-error/53-matcher-alias-specialize.egi view
@@ -0,0 +1,8 @@+-- Matcher rigidity: a polymorphic matcher value cannot be re-typed at a+-- concrete matcher type by annotation.  eq has the intrinsic type+-- {Eq a} => Matcher a; binding it at Matcher Integer would make the type+-- system believe a capability the value does not change.  The standard+-- library instead inlines eq's body as a concrete matcher literal+-- (T-MATCHER in checking mode derives the capability at the declared type).++def myint : Matcher Integer := eq
+ test/type-error/54-something-structured-hole.egi view
@@ -0,0 +1,13 @@+-- Deferred hole admissibility (paper PP-Con, Def 4.2(1a)): the tail hole of+-- the cons clause resolves (only via the annotation) to [Integer], a+-- constructor-headed type, where the bare-variable matcher `something` is+-- not structurally admissible.  The check runs after the definition's final+-- substitution, so the late pinning does not escape it.++def bad : Matcher [Integer] :=+  matcher+    | $ :: $ as (something, something) with+      | $x :: $xs -> [(x, xs)]+      | _ -> []+    | $ as something with+      | $tgt -> [tgt]
+ test/type-error/55-multisite-target-conflict.egi view
@@ -0,0 +1,12 @@+--+-- Multi-site matcher parameter, conflicting TARGET types (paper Appendix C,+-- Step 3a): the second match site component-unifies the committed slot, so+-- its target type must equal the first site's.  A lambda-bound matcher is+-- monomorphic (standard HM), so [Integer] vs [String] is rejected.+--+-- Expected: Type error at the second match site (Integer vs String)+--++def h m :=+  ( matchAll [1, 2] as m with $x :: _ -> x+  , matchAll ["a", "b"] as m with $y :: _ -> y )
+ test/type-error/56-multisite-structural-join.egi view
@@ -0,0 +1,15 @@+--+-- Multi-site matcher parameter, joint structural demand (paper Appendix C,+-- Step 3a): site 1's cons pattern makes the committed slot's structural+-- index list-headed; the value-pattern-only site 2 adds no demand but does+-- not erase it.  Supplying `something` at the application then fails the+-- structural half of COERCE-MATCHER-TO-SLOT.+--+-- Expected: Type error at `g something` (bare variable against a list head)+--++def g m :=+  ( matchAll [1, 2] as m with $x :: _ -> x+  , matchAll [1, 2] as m with #[1, 2] -> True )++def t := g something
+ test/type-error/README.md view
@@ -0,0 +1,64 @@+# test/type-error — 型検査の拒否(reject)適合テスト++このディレクトリの各 `.egi` ファイルは、**型検査がエラーを検知すべき**プログラムを+1ファイル1ケースで収めたものです。`mini-test/` が受理側(型 clean + 実行結果)の+回帰テストであるのに対し、こちらは拒否側の適合テストです+(`design/paper-compliance-roadmap.md` 課題 H の reject 側)。+各ファイルの先頭コメントに、対応する論文の規則・出典と期待されるエラーを記しています。++## 検証方法++`-t` は permissive モード(型エラーを出しても untyped 評価にフォールバックして exit 0)+なので、**exit code ではなく出力の `Type error:` を grep** して判定します。+全ファイルが「`Type error:` を含み、`Parse error` を含まない」ことが合格条件です:++```sh+fail=0+for f in test/type-error/*.egi; do+  o=$(gtimeout -k 10 60 cabal run -v0 egison -- -t "$f" 2>&1)+  echo "$o" | grep -q "Type error:" || { echo "MISSING ERROR: $f"; fail=1; }+  echo "$o" | grep -q "Parse error" && { echo "PARSE ERROR: $f"; fail=1; }+done+[ $fail -eq 0 ] && echo "all rejected as expected"+```++## ケース一覧++| ファイル | 規則 / 出典 | 内容 |+|---|---|---|+| 01-something-cons | COERCE-MATCHER-TO-SLOT(論文 B Case 2) | `something` × cons パターン |+| 02-something-cons-param | 同(B Case 3) | 関数パラメータ経由、**適用点**で拒否 |+| 03-nested-ctor-element | PAT-CON の構造伝播(B.2.3) | `multiset something` × ネスト構築子 `num` |+| 04-something-tuple-pattern | MS Progress のタプルケース | `something` × タプルパターン(積型頭の slot) |+| 05-matcher-target-mismatch | ターゲット不一致(B.2.1) | `num` パターン × `[Integer]` |+| 06-target-type-mismatch | T-MATCHALL のターゲット側 | `matchAll 5 as multiset integer` |+| 10-patfun-body-structural | PAT-APP 構造側・本体(レビュー反例 M1) | `pair $x []` × `something` |+| 11-patfun-arg-structural | PAT-APP 構造側・引数(M1) | `idp ($x :: $xs)` × `something` |+| 12-patfun-nested-arg-structural | PAT-APP 構造側・ネスト引数(B.2.3) | `pair (num $n) []` × `multiset something` |+| 13-patfun-target | PAT-APP ターゲット側 | `seqp`(`[Tile]`)× `[Integer]` ターゲット |+| 14-patfun-arg-target | PAT-APP 引数ターゲット(B.1.2) | `seqp #1 _`(`Integer` vs `Tile`) |+| 20-patfun-linearity-unused | PATFUN-DEF 線形性(M2) | 未使用パラメータ |+| 21-patfun-linearity-order | 同 | 宣言順違反 |+| 22-patfun-linearity-dup | 同 | 重複使用 |+| 23-patfun-linearity-under-or | 同 | or 分岐配下での使用 |+| 30-value-pattern-expr-type | PAT-VALUE(B.1.1) | 値パターン内式の型エラー(`x ++ [1]`) |+| 31-nonlinear-target-type | 非線形パターン(B.1.1) | `$x :: #x`(要素 vs リスト) |+| 32-or-pattern-bindings | PAT-OR | 分岐間の束縛変数不一致 |+| 40-matcher-next-structural | Def 4.2(1a) / PP-Con(B の `weird`) | 構築子頭 hole への `something` |+| 41-matcher-body-matchsite | 本体内 match-site 検査 | matcher 本体内の `integer` × cons |+| 42-tuple-pattern-arity | PAT-TUPLE | タプルパターンの arity 不一致 |+| 50-matcher-collection-hetero | Matcher rigidity | `[something, list integer]`(異種 matcher のコレクション) |+| 51-matcher-cast-structured | Matcher rigidity | `def m2 : Matcher [Integer] := something`(構造型への束縛) |+| 52-missing-signature-constraint | シグネチャ完全性(残存制約検査) | 本体が `<=`({Ord a})を要求するのにシグネチャに無い |+| 53-matcher-alias-specialize | Matcher rigidity | `def myint : Matcher Integer := eq`(注釈による特殊化) |+| 54-something-structured-hole | PP-Con 遅延判定 | 注釈で後から [Integer] に確定する hole への `something` |+| 55-multisite-target-conflict | Algorithm W Step 3a(複数 match site) | λ束縛 matcher を `[Integer]` と `[String]` の2 site で使用(単相なので拒否) |+| 56-multisite-structural-join | 同(構造要求の join) | site 1 の cons 要求が commit 済み slot に残り、`g something` が適用点で拒否 |++## ケース追加時の注意++- 1ファイル1ケース。先頭コメントに対応規則と期待エラーを書く。+- 追加時は必ず実行して、**意図したエラーで**拒否されることを確認する+  (無関係なエラーや parse error で偶然 reject されると回帰検出にならない)。+- 受理側の対になるケースがあれば `mini-test/` に置く+  (例: `mini-test/120-patfun-struct-index.egi`)。