mirror of
https://github.com/typst/typst
synced 2025-05-22 13:05:29 +08:00
Better primes in math (#1614)
This commit is contained in:
parent
be0f8fe6d7
commit
dfe361ec6a
@ -120,6 +120,7 @@ fn items() -> LangItems {
|
|||||||
}
|
}
|
||||||
elem.pack()
|
elem.pack()
|
||||||
},
|
},
|
||||||
|
math_primes: |count| math::PrimesElem::new(count).pack(),
|
||||||
math_accent: |base, accent| {
|
math_accent: |base, accent| {
|
||||||
math::AccentElem::new(base, math::Accent::new(accent)).pack()
|
math::AccentElem::new(base, math::Accent::new(accent)).pack()
|
||||||
},
|
},
|
||||||
|
@ -84,6 +84,61 @@ impl LayoutMath for AttachElem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Grouped primes.
|
||||||
|
///
|
||||||
|
/// ## Example { #example }
|
||||||
|
/// ```example
|
||||||
|
/// $ a'''_b = a^'''_b $
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Syntax
|
||||||
|
/// This function has dedicated syntax: use apostrophes instead of primes. They
|
||||||
|
/// will automatically attach to the previous element, moving superscripts to
|
||||||
|
/// the next level.
|
||||||
|
///
|
||||||
|
/// Display: Attachment
|
||||||
|
/// Category: math
|
||||||
|
#[element(LayoutMath)]
|
||||||
|
pub struct PrimesElem {
|
||||||
|
/// The number of grouped primes.
|
||||||
|
#[required]
|
||||||
|
pub count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutMath for PrimesElem {
|
||||||
|
#[tracing::instrument(skip(ctx))]
|
||||||
|
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
|
||||||
|
match self.count() {
|
||||||
|
count @ 1..=4 => {
|
||||||
|
let f = ctx.layout_fragment(&TextElem::packed(match count {
|
||||||
|
1 => '′',
|
||||||
|
2 => '″',
|
||||||
|
3 => '‴',
|
||||||
|
4 => '⁗',
|
||||||
|
_ => unreachable!(),
|
||||||
|
}))?;
|
||||||
|
ctx.push(f);
|
||||||
|
}
|
||||||
|
count => {
|
||||||
|
// Custom amount of primes
|
||||||
|
let prime = ctx.layout_fragment(&TextElem::packed('′'))?.into_frame();
|
||||||
|
let width = prime.width() * (count + 1) as f64 / 2.0;
|
||||||
|
let mut frame = Frame::new(Size::new(width, prime.height()));
|
||||||
|
frame.set_baseline(prime.ascent());
|
||||||
|
|
||||||
|
for i in 0..count {
|
||||||
|
frame.push_frame(
|
||||||
|
Point::new(prime.width() * (i as f64 / 2.0), Abs::zero()),
|
||||||
|
prime.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ctx.push(FrameFragment::new(ctx, frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Forces a base to display attachments as scripts.
|
/// Forces a base to display attachments as scripts.
|
||||||
///
|
///
|
||||||
/// ## Example { #example }
|
/// ## Example { #example }
|
||||||
|
@ -96,6 +96,8 @@ pub struct LangItems {
|
|||||||
tr: Option<Content>,
|
tr: Option<Content>,
|
||||||
br: Option<Content>,
|
br: Option<Content>,
|
||||||
) -> Content,
|
) -> Content,
|
||||||
|
/// Grouped primes: `a'''`.
|
||||||
|
pub math_primes: fn(count: usize) -> Content,
|
||||||
/// A base with an accent: `arrow(x)`.
|
/// A base with an accent: `arrow(x)`.
|
||||||
pub math_accent: fn(base: Content, accent: char) -> Content,
|
pub math_accent: fn(base: Content, accent: char) -> Content,
|
||||||
/// A fraction in math: `x/2`.
|
/// A fraction in math: `x/2`.
|
||||||
|
@ -460,6 +460,7 @@ impl Eval for ast::Expr {
|
|||||||
Self::MathAlignPoint(v) => v.eval(vm).map(Value::Content),
|
Self::MathAlignPoint(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::MathDelimited(v) => v.eval(vm).map(Value::Content),
|
Self::MathDelimited(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::MathAttach(v) => v.eval(vm).map(Value::Content),
|
Self::MathAttach(v) => v.eval(vm).map(Value::Content),
|
||||||
|
Self::MathPrimes(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::MathFrac(v) => v.eval(vm).map(Value::Content),
|
Self::MathFrac(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::MathRoot(v) => v.eval(vm).map(Value::Content),
|
Self::MathRoot(v) => v.eval(vm).map(Value::Content),
|
||||||
Self::Ident(v) => v.eval(vm),
|
Self::Ident(v) => v.eval(vm),
|
||||||
@ -733,12 +734,28 @@ impl Eval for ast::MathAttach {
|
|||||||
#[tracing::instrument(name = "MathAttach::eval", skip_all)]
|
#[tracing::instrument(name = "MathAttach::eval", skip_all)]
|
||||||
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
let base = self.base().eval_display(vm)?;
|
let base = self.base().eval_display(vm)?;
|
||||||
let top = self.top().map(|expr| expr.eval_display(vm)).transpose()?;
|
|
||||||
|
let mut top = self.top().map(|expr| expr.eval_display(vm)).transpose()?;
|
||||||
|
if top.is_none() {
|
||||||
|
if let Some(primes) = self.primes() {
|
||||||
|
top = Some(primes.eval(vm)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let bottom = self.bottom().map(|expr| expr.eval_display(vm)).transpose()?;
|
let bottom = self.bottom().map(|expr| expr.eval_display(vm)).transpose()?;
|
||||||
Ok((vm.items.math_attach)(base, top, bottom, None, None, None, None))
|
Ok((vm.items.math_attach)(base, top, bottom, None, None, None, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eval for ast::MathPrimes {
|
||||||
|
type Output = Content;
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "MathPrimes::eval", skip_all)]
|
||||||
|
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
|
||||||
|
Ok((vm.items.math_primes)(self.count()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Eval for ast::MathFrac {
|
impl Eval for ast::MathFrac {
|
||||||
type Output = Content;
|
type Output = Content;
|
||||||
|
|
||||||
|
@ -147,6 +147,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
|
|||||||
SyntaxKind::MathAttach => None,
|
SyntaxKind::MathAttach => None,
|
||||||
SyntaxKind::MathFrac => None,
|
SyntaxKind::MathFrac => None,
|
||||||
SyntaxKind::MathRoot => None,
|
SyntaxKind::MathRoot => None,
|
||||||
|
SyntaxKind::MathPrimes => None,
|
||||||
|
|
||||||
SyntaxKind::Hashtag => highlight_hashtag(node),
|
SyntaxKind::Hashtag => highlight_hashtag(node),
|
||||||
SyntaxKind::LeftBrace => Some(Tag::Punctuation),
|
SyntaxKind::LeftBrace => Some(Tag::Punctuation),
|
||||||
@ -174,6 +175,7 @@ pub fn highlight(node: &LinkedNode) -> Option<Tag> {
|
|||||||
_ => Tag::Operator,
|
_ => Tag::Operator,
|
||||||
}),
|
}),
|
||||||
SyntaxKind::Hat => Some(Tag::MathOperator),
|
SyntaxKind::Hat => Some(Tag::MathOperator),
|
||||||
|
SyntaxKind::Prime => Some(Tag::MathOperator),
|
||||||
SyntaxKind::Dot => Some(Tag::Punctuation),
|
SyntaxKind::Dot => Some(Tag::Punctuation),
|
||||||
SyntaxKind::Eq => match node.parent_kind() {
|
SyntaxKind::Eq => match node.parent_kind() {
|
||||||
Some(SyntaxKind::Heading) => None,
|
Some(SyntaxKind::Heading) => None,
|
||||||
|
@ -124,6 +124,8 @@ pub enum Expr {
|
|||||||
MathDelimited(MathDelimited),
|
MathDelimited(MathDelimited),
|
||||||
/// A base with optional attachments in math: `a_1^2`.
|
/// A base with optional attachments in math: `a_1^2`.
|
||||||
MathAttach(MathAttach),
|
MathAttach(MathAttach),
|
||||||
|
/// Grouped math primes
|
||||||
|
MathPrimes(MathPrimes),
|
||||||
/// A fraction in math: `x/2`.
|
/// A fraction in math: `x/2`.
|
||||||
MathFrac(MathFrac),
|
MathFrac(MathFrac),
|
||||||
/// A root in math: `√x`, `∛x` or `∜x`.
|
/// A root in math: `√x`, `∛x` or `∜x`.
|
||||||
@ -224,6 +226,7 @@ impl AstNode for Expr {
|
|||||||
SyntaxKind::MathAlignPoint => node.cast().map(Self::MathAlignPoint),
|
SyntaxKind::MathAlignPoint => node.cast().map(Self::MathAlignPoint),
|
||||||
SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
|
SyntaxKind::MathDelimited => node.cast().map(Self::MathDelimited),
|
||||||
SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
|
SyntaxKind::MathAttach => node.cast().map(Self::MathAttach),
|
||||||
|
SyntaxKind::MathPrimes => node.cast().map(Self::MathPrimes),
|
||||||
SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
|
SyntaxKind::MathFrac => node.cast().map(Self::MathFrac),
|
||||||
SyntaxKind::MathRoot => node.cast().map(Self::MathRoot),
|
SyntaxKind::MathRoot => node.cast().map(Self::MathRoot),
|
||||||
SyntaxKind::Ident => node.cast().map(Self::Ident),
|
SyntaxKind::Ident => node.cast().map(Self::Ident),
|
||||||
@ -285,6 +288,7 @@ impl AstNode for Expr {
|
|||||||
Self::MathAlignPoint(v) => v.as_untyped(),
|
Self::MathAlignPoint(v) => v.as_untyped(),
|
||||||
Self::MathDelimited(v) => v.as_untyped(),
|
Self::MathDelimited(v) => v.as_untyped(),
|
||||||
Self::MathAttach(v) => v.as_untyped(),
|
Self::MathAttach(v) => v.as_untyped(),
|
||||||
|
Self::MathPrimes(v) => v.as_untyped(),
|
||||||
Self::MathFrac(v) => v.as_untyped(),
|
Self::MathFrac(v) => v.as_untyped(),
|
||||||
Self::MathRoot(v) => v.as_untyped(),
|
Self::MathRoot(v) => v.as_untyped(),
|
||||||
Self::Ident(v) => v.as_untyped(),
|
Self::Ident(v) => v.as_untyped(),
|
||||||
@ -841,6 +845,25 @@ impl MathAttach {
|
|||||||
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
|
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
|
||||||
.find_map(SyntaxNode::cast)
|
.find_map(SyntaxNode::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract primes if present.
|
||||||
|
pub fn primes(&self) -> Option<MathPrimes> {
|
||||||
|
self.0.cast_first_match()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node! {
|
||||||
|
/// Grouped primes in math: `a'''`.
|
||||||
|
MathPrimes
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MathPrimes {
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
self.0
|
||||||
|
.children()
|
||||||
|
.filter(|node| matches!(node.kind(), SyntaxKind::Prime))
|
||||||
|
.count()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node! {
|
node! {
|
||||||
|
@ -65,6 +65,8 @@ pub enum SyntaxKind {
|
|||||||
MathDelimited,
|
MathDelimited,
|
||||||
/// A base with optional attachments in math: `a_1^2`.
|
/// A base with optional attachments in math: `a_1^2`.
|
||||||
MathAttach,
|
MathAttach,
|
||||||
|
/// Grouped primes in math: `a'''`.
|
||||||
|
MathPrimes,
|
||||||
/// A fraction in math: `x/2`.
|
/// A fraction in math: `x/2`.
|
||||||
MathFrac,
|
MathFrac,
|
||||||
/// A root in math: `√x`, `∛x` or `∜x`.
|
/// A root in math: `√x`, `∛x` or `∜x`.
|
||||||
@ -108,6 +110,8 @@ pub enum SyntaxKind {
|
|||||||
Slash,
|
Slash,
|
||||||
/// The superscript operator in math: `^`.
|
/// The superscript operator in math: `^`.
|
||||||
Hat,
|
Hat,
|
||||||
|
/// The prime in math: `'`.
|
||||||
|
Prime,
|
||||||
/// The field access and method call operator: `.`.
|
/// The field access and method call operator: `.`.
|
||||||
Dot,
|
Dot,
|
||||||
/// The assignment operator: `=`.
|
/// The assignment operator: `=`.
|
||||||
@ -378,6 +382,7 @@ impl SyntaxKind {
|
|||||||
Self::MathAttach => "math attachments",
|
Self::MathAttach => "math attachments",
|
||||||
Self::MathFrac => "math fraction",
|
Self::MathFrac => "math fraction",
|
||||||
Self::MathRoot => "math root",
|
Self::MathRoot => "math root",
|
||||||
|
Self::MathPrimes => "math primes",
|
||||||
Self::Hashtag => "hashtag",
|
Self::Hashtag => "hashtag",
|
||||||
Self::LeftBrace => "opening brace",
|
Self::LeftBrace => "opening brace",
|
||||||
Self::RightBrace => "closing brace",
|
Self::RightBrace => "closing brace",
|
||||||
@ -395,6 +400,7 @@ impl SyntaxKind {
|
|||||||
Self::Minus => "minus",
|
Self::Minus => "minus",
|
||||||
Self::Slash => "slash",
|
Self::Slash => "slash",
|
||||||
Self::Hat => "hat",
|
Self::Hat => "hat",
|
||||||
|
Self::Prime => "prime",
|
||||||
Self::Dot => "dot",
|
Self::Dot => "dot",
|
||||||
Self::Eq => "equals sign",
|
Self::Eq => "equals sign",
|
||||||
Self::EqEq => "equality operator",
|
Self::EqEq => "equality operator",
|
||||||
|
@ -422,13 +422,14 @@ impl Lexer<'_> {
|
|||||||
'|' if self.s.eat_if('|') => SyntaxKind::Shorthand,
|
'|' if self.s.eat_if('|') => SyntaxKind::Shorthand,
|
||||||
'~' if self.s.eat_if("~>") => SyntaxKind::Shorthand,
|
'~' if self.s.eat_if("~>") => SyntaxKind::Shorthand,
|
||||||
'~' if self.s.eat_if('>') => SyntaxKind::Shorthand,
|
'~' if self.s.eat_if('>') => SyntaxKind::Shorthand,
|
||||||
'*' | '\'' | '-' => SyntaxKind::Shorthand,
|
'*' | '-' => SyntaxKind::Shorthand,
|
||||||
|
|
||||||
'#' => SyntaxKind::Hashtag,
|
'#' => SyntaxKind::Hashtag,
|
||||||
'_' => SyntaxKind::Underscore,
|
'_' => SyntaxKind::Underscore,
|
||||||
'$' => SyntaxKind::Dollar,
|
'$' => SyntaxKind::Dollar,
|
||||||
'/' => SyntaxKind::Slash,
|
'/' => SyntaxKind::Slash,
|
||||||
'^' => SyntaxKind::Hat,
|
'^' => SyntaxKind::Hat,
|
||||||
|
'\'' => SyntaxKind::Prime,
|
||||||
'&' => SyntaxKind::MathAlignPoint,
|
'&' => SyntaxKind::MathAlignPoint,
|
||||||
'√' | '∛' | '∜' => SyntaxKind::Root,
|
'√' | '∛' | '∜' => SyntaxKind::Root,
|
||||||
|
|
||||||
|
@ -295,6 +295,18 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SyntaxKind::Prime => {
|
||||||
|
// Means that there is nothing to attach the prime to.
|
||||||
|
continuable = true;
|
||||||
|
while p.at(SyntaxKind::Prime) {
|
||||||
|
let m2 = p.marker();
|
||||||
|
p.eat();
|
||||||
|
// Eat the group until the space.
|
||||||
|
while p.eat_if_direct(SyntaxKind::Prime) {}
|
||||||
|
p.wrap(m2, SyntaxKind::MathPrimes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => p.expected("expression"),
|
_ => p.expected("expression"),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,6 +318,9 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
p.wrap(m, SyntaxKind::Math);
|
p.wrap(m, SyntaxKind::Math);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether there were _any_ primes in the loop.
|
||||||
|
let mut primed = false;
|
||||||
|
|
||||||
while !p.eof() && !p.at(stop) {
|
while !p.eof() && !p.at(stop) {
|
||||||
if p.directly_at(SyntaxKind::Text) && p.current_text() == "!" {
|
if p.directly_at(SyntaxKind::Text) && p.current_text() == "!" {
|
||||||
p.eat();
|
p.eat();
|
||||||
@ -313,10 +328,39 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let prime_marker = p.marker();
|
||||||
|
if p.eat_if_direct(SyntaxKind::Prime) {
|
||||||
|
// Eat as many primes as possible.
|
||||||
|
while p.eat_if_direct(SyntaxKind::Prime) {}
|
||||||
|
p.wrap(prime_marker, SyntaxKind::MathPrimes);
|
||||||
|
|
||||||
|
// Will not be continued, so need to wrap the prime as attachment.
|
||||||
|
if p.at(stop) {
|
||||||
|
p.wrap(m, SyntaxKind::MathAttach);
|
||||||
|
}
|
||||||
|
|
||||||
|
primed = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separate primes and superscripts to different attachments.
|
||||||
|
if primed && p.current() == SyntaxKind::Hat {
|
||||||
|
p.wrap(m, SyntaxKind::MathAttach);
|
||||||
|
}
|
||||||
|
|
||||||
let Some((kind, stop, assoc, mut prec)) = math_op(p.current()) else {
|
let Some((kind, stop, assoc, mut prec)) = math_op(p.current()) else {
|
||||||
|
// No attachments, so we need to wrap primes as attachment.
|
||||||
|
if primed {
|
||||||
|
p.wrap(m, SyntaxKind::MathAttach);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if primed && kind == SyntaxKind::MathFrac {
|
||||||
|
p.wrap(m, SyntaxKind::MathAttach);
|
||||||
|
}
|
||||||
|
|
||||||
if prec < min_prec {
|
if prec < min_prec {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -335,7 +379,7 @@ fn math_expr_prec(p: &mut Parser, min_prec: usize, stop: SyntaxKind) {
|
|||||||
math_expr_prec(p, prec, stop);
|
math_expr_prec(p, prec, stop);
|
||||||
math_unparen(p, m2);
|
math_unparen(p, m2);
|
||||||
|
|
||||||
if p.eat_if(SyntaxKind::Underscore) || p.eat_if(SyntaxKind::Hat) {
|
if p.eat_if(SyntaxKind::Underscore) || (!primed && p.eat_if(SyntaxKind::Hat)) {
|
||||||
let m3 = p.marker();
|
let m3 = p.marker();
|
||||||
math_expr_prec(p, prec, SyntaxKind::Eof);
|
math_expr_prec(p, prec, SyntaxKind::Eof);
|
||||||
math_unparen(p, m3);
|
math_unparen(p, m3);
|
||||||
@ -1451,6 +1495,10 @@ impl<'s> Parser<'s> {
|
|||||||
self.current == kind && self.prev_end == self.current_start
|
self.current == kind && self.prev_end == self.current_start
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Eats if at `kind`.
|
||||||
|
///
|
||||||
|
/// Note: In math and code mode, this will ignore trivia in front of the
|
||||||
|
/// `kind`, To forbid skipping trivia, consider using `eat_if_direct`.
|
||||||
fn eat_if(&mut self, kind: SyntaxKind) -> bool {
|
fn eat_if(&mut self, kind: SyntaxKind) -> bool {
|
||||||
let at = self.at(kind);
|
let at = self.at(kind);
|
||||||
if at {
|
if at {
|
||||||
@ -1459,6 +1507,15 @@ impl<'s> Parser<'s> {
|
|||||||
at
|
at
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Eats only if currently at the start of `kind`.
|
||||||
|
fn eat_if_direct(&mut self, kind: SyntaxKind) -> bool {
|
||||||
|
let at = self.directly_at(kind);
|
||||||
|
if at {
|
||||||
|
self.eat();
|
||||||
|
}
|
||||||
|
at
|
||||||
|
}
|
||||||
|
|
||||||
fn convert(&mut self, kind: SyntaxKind) {
|
fn convert(&mut self, kind: SyntaxKind) {
|
||||||
self.current = kind;
|
self.current = kind;
|
||||||
self.eat();
|
self.eat();
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.8 KiB |
@ -28,3 +28,18 @@ $sum_(k in NN)^prime 1/k^2$
|
|||||||
$ 1/(x^A) $
|
$ 1/(x^A) $
|
||||||
#[#set text(size:18pt); $1/(x^A)$] vs. #[#set text(size:14pt); $x^A$]
|
#[#set text(size:18pt); $1/(x^A)$] vs. #[#set text(size:14pt); $x^A$]
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test dedicated syntax for primes
|
||||||
|
$a'$, $a'''_b$, $'$, $'''''''$
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test spaces between
|
||||||
|
$a' ' '$, $' ' '$, $a' '/b$
|
||||||
|
|
||||||
|
---
|
||||||
|
// Test complex prime combilnations
|
||||||
|
$a'_b^c$, $a_b'^c$, $a_b^c'$, $a_b'^c'^d'$
|
||||||
|
|
||||||
|
$(a'_b')^(c'_d')$, $a'/b'$, $a_b'/c_d'$
|
||||||
|
|
||||||
|
$∫'$, $∑'$, $ ∑'_S' $
|
||||||
|
Loading…
x
Reference in New Issue
Block a user