summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/latex/longdivision
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2017-02-06 23:37:59 +0000
committerKarl Berry <karl@freefriends.org>2017-02-06 23:37:59 +0000
commiteb1b06cb07745cb30557b035fa78e1bd90c5df9a (patch)
treef0a02b3460ecca0eefc41756ef5a5b6eeb294fee /Master/texmf-dist/tex/latex/longdivision
parent3b82837a4c3eea1141abcab4c26823c64a671fe1 (diff)
longdivision (6feb17)
git-svn-id: svn://tug.org/texlive/trunk@43159 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/longdivision')
-rw-r--r--Master/texmf-dist/tex/latex/longdivision/longdivision.sty485
1 files changed, 485 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/latex/longdivision/longdivision.sty b/Master/texmf-dist/tex/latex/longdivision/longdivision.sty
new file mode 100644
index 00000000000..e25d7671b8b
--- /dev/null
+++ b/Master/texmf-dist/tex/latex/longdivision/longdivision.sty
@@ -0,0 +1,485 @@
+%% Package: longdivision.sty version 1.0
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2/5/2017
+%% License: Latex Project Public License
+
+
+\ProvidesPackage{longdivision}
+\RequirePackage{xparse}
+
+\ExplSyntaxOn
+
+%%
+%% The linked list
+%%
+
+% This token list just stores a reference to the first entry in the linked list
+\tl_new:N \l__longdiv_linkedlist_tl
+\tl_set:Nn\l__longdiv_linkedlist_tl{ \longdiv_linkedlist_next:n { 0 } }
+\int_new:N \l__longdiv_linkedlist_length_int
+
+% Set the next entry to be a no-op so that when expanded the last "null pointer" just disappears
+\cs_new:Nn \longdiv_linkedlist_set_next_do_nothing: {
+ \cs_set_eq:cN { longdiv_linkedlist ~ \int_use:N \l__longdiv_linkedlist_length_int } \prg_do_nothing:
+}
+\longdiv_linkedlist_set_next_do_nothing:
+
+% "pointer" to next element (argument is the element's id)
+\cs_new:Nn \longdiv_linkedlist_next:n { \use:c { longdiv_linkedlist ~ #1 } }
+
+\cs_new:Nn \longdiv_linkedlist_add:n {
+ \cs_set:cpx { longdiv_linkedlist ~ \int_use:N \l__longdiv_linkedlist_length_int}{
+ \exp_not:n{#1} \exp_not:N \longdiv_linkedlist_next:n { \int_eval:n { \l__longdiv_linkedlist_length_int + 1} }
+ }
+ \int_incr:N \l__longdiv_linkedlist_length_int
+ \longdiv_linkedlist_set_next_do_nothing:
+}
+\cs_generate_variant:Nn \longdiv_linkedlist_add:n {f}
+
+% The easy implementation of these next two commands is why I chose the "linked list" format
+
+% Delete last element of list.
+\cs_new:Nn \longdiv_linkedlist_remove_tail: {
+ \int_decr:N \l__longdiv_linkedlist_length_int
+ \longdiv_linkedlist_set_next_do_nothing:
+}
+
+% #1 -- pointer to an element of the list
+% Puts an overline over the rest of the list starting at a certain position. For "repeating decimals".
+\cs_new:Nn \longdiv_linkedlist_put_overline:n {
+ \cs_set:cpx { longdiv_linkedlist ~ #1 }{
+ $\overline { \exp_not:f { \use:c { longdiv_linkedlist ~ #1 } } }$
+ }
+}
+
+
+%%
+%% Entry points
+%%
+
+% \tl_rescan to ignore spaces in input.
+\NewDocumentCommand \longdivision { omm } {
+ \group_begin:
+ \IfNoValueF { #1 } {
+ \int_set:Nn \l__longdiv_max_extra_digits_int { #1 }
+ }
+ \tl_rescan:nn { \ExplSyntaxOn } { \longdiv_start:nn { #2 } { #3 } }
+ \group_end:
+}
+
+% Same as \longdiv[0]{#1}{#2}.
+\NewDocumentCommand \intlongdivision { mm } {
+ \group_begin:
+ \int_set:Nn \l__longdiv_max_extra_digits_int { 0 }
+ \tl_rescan:nn { \ExplSyntaxOn } { \longdiv_start:nn { #1 } { #2 } }
+ \group_end:
+}
+
+% Check input is valid then enter main loop.
+% We use etex \numexpr to ensure that the dividend has no unnecessary leading zeroes and doesn't begin with a decimal point.
+% Note that \int_eval:n wouldn't work here because it inserts a "\relax" token that would not get eaten by \numexpr if
+% #1 contains a decimal point. This "\relax" causes trouble for the division main loop.
+\cs_new:Nn \longdiv_start:nn {
+ \longdiv_check_dividend:n { #1 }
+ \longdiv_check_divisor:n { #2 }
+ % Second copy of #1 is eaten by \longdiv_typeset:nnn to print the dividend
+ \exp_args:Nnnff \longdiv_get_new_digit:nnn { } { #2 } { \the\numexpr 0#1 } { \the\numexpr 0#1 }
+ \longdiv_break_point: { #1 } { #2 }
+}
+
+\cs_set_eq:NN \longdiv_break_point: \use_none:nn
+
+%%
+%% Input checkers
+%%
+
+% Parse through the dividend token by token
+% Check that every token is a digit with the exception of at most one .
+\cs_new:Nn \longdiv_check_dividend:n {
+ \longdiv_check_dividend_before_point:N #1 \q_stop
+}
+
+\cs_new:Nn \longdiv_check_dividend_before_point:N {
+ \token_if_eq_meaning:NNF #1 \q_stop {
+ \token_if_eq_meaning:NNTF #1 . {
+ \longdiv_check_dividend_seen_point:N
+ }{
+ \longdiv_check_dividend_isdigit:N #1
+ \longdiv_check_dividend_before_point:N
+ }
+ }
+}
+
+\cs_new:Nn \longdiv_check_dividend_seen_point:N {
+ \token_if_eq_meaning:NNF #1 \q_stop {
+ \longdiv_check_dividend_isdigit:N #1
+ \longdiv_check_dividend_seen_point:N
+ }
+}
+
+\cs_new:Nn \longdiv_check_dividend_isdigit:N {
+ \bool_if:nF { \token_if_eq_meaning_p:NN #1 0
+ || \token_if_eq_meaning_p:NN #1 1 || \token_if_eq_meaning_p:NN #1 2 || \token_if_eq_meaning_p:NN #1 3
+ || \token_if_eq_meaning_p:NN #1 4 || \token_if_eq_meaning_p:NN #1 5 || \token_if_eq_meaning_p:NN #1 6
+ || \token_if_eq_meaning_p:NN #1 7 || \token_if_eq_meaning_p:NN #1 8 || \token_if_eq_meaning_p:NN #1 9
+ }{
+ \longdiv_error:nwnn { dividend_invalid }
+ }
+}
+
+% Check that there is no ., that it is at most 8 digits, and that the entire argument can get assigned to a count variable
+% There's no way to do this last check in expl3, so I use plaintex \newcount, \afterassignment, and \l__longdiv_temp_int =.
+\newcount \l__longdiv_temp_int
+\cs_new:Nn \longdiv_check_divisor:n {
+ \tl_if_in:nnT { #1 } { . } {
+ \longdiv_error:nwnn { divisor_not_int }
+ }
+ % We have to do the length check before the "validity" check because the "validity" check makes an assignment
+ % which throws a low level error if the number to be assigned is too large.
+ \int_compare:nNnF { \tl_count:n { #1 } } < \c_nine {
+ \longdiv_error:nwnn { divisor_too_large }
+ }
+ % Idea here: if #1 is a valid number, \l__longdiv_temp_int = 0#1 will absorb all of it.
+ % So if there's any left, throw an error. Leading zero ensures that it fails on -1 and
+ % that if #1 starts with some other nondigit character that it won't cause
+ % "Missing number, treated as zero."
+ \afterassignment \longdiv_check_divisor_aux:w
+ \l__longdiv_temp_int = 0 #1 \scan_stop:
+}
+
+\cs_new:Npn \longdiv_check_divisor_aux:w #1 \scan_stop: {
+ \tl_if_empty:nF { #1 } {
+ \longdiv_error:nwnn { divisor_invalid }
+ }
+ \int_compare:nNnT \l__longdiv_temp_int = \c_zero {
+ \longdiv_error:nwnn { divisor_zero }
+ }
+}
+
+% Absorb up to break_point to gracefully quit out of the macro
+\cs_new:Npn \longdiv_error:nwnn #1 #2 \longdiv_break_point: {
+ \msg_error:nnnn { longdivision } { #1 }
+}
+
+% Errors:
+\msg_new:nnn { longdivision } { dividend_invalid } { Dividend ~ '#1' ~ is ~ invalid ~ (\msg_line_context:).}
+\msg_new:nnn { longdivision } { divisor_too_large }
+ { Divisor ~ '#2' ~ is ~ too ~ large ~ (\msg_line_context:). ~ It ~ has ~ \tl_count:n { #2 } ~ digits, ~ but ~ divisors ~ can ~ be ~ at ~ most ~ 9 ~ digits ~ long. }
+\msg_new:nnn { longdivision } { divisor_not_int } { Divisor ~ '#2' ~ is ~ not ~ an ~ integer ~ (\msg_line_context:). }
+\msg_new:nnn { longdivision } { divisor_invalid } { Divisor ~ '#2' ~ is ~ invalid ~ (\msg_line_context:). }
+
+% Warnings:
+\msg_new:nnn { longdivision } { work_stopped_early } { The ~ work ~ display ~ stopped ~ early ~ to ~ avoid ~ running ~ off ~ the ~ page ~ (\msg_line_context:). }
+\msg_new:nnn { longdivision } { division_stopped_early } { The ~ division ~ stopped ~ early ~ to ~ avoid ~ running ~ off ~ the ~ page ~ (\msg_line_context:).}
+\msg_new:nnn { longdivision } { no_division_occurred }
+ { Either ~ the ~ dividend ~ was ~ zero ~ or ~ you ~ used ~ \token_to_str:N \intlongdiv \space and ~ the ~ dividend ~ was ~less ~ than ~ the ~ divisor. ~
+ This ~ isn't ~ a ~ big ~ deal, ~ but ~ the ~ result ~ probably ~ looks ~ silly. }
+
+
+
+%%
+%% Division
+%%
+
+% Core registers
+\bool_new:N \l__longdiv_seen_point_bool
+\bool_new:N \l__longdiv_seen_digit_bool
+\int_new:N \l__longdiv_quotient_int
+\int_new:N \l__longdiv_position_int
+\int_new:N \l__longdiv_point_digit_int
+
+\tl_new:N \l__longdiv_extra_places_tl % This is for storing extra zeroes appended to the dividend
+\dim_new:N \g__longdiv_temp_dim % For measuring the distance to the right side of digits
+
+% These are used to make sure division doesn't run off the page.
+\int_new:N \l__longdiv_extra_digits_int
+\int_new:N \l__longdiv_max_extra_digits_int
+\int_set:Nn \l__longdiv_max_extra_digits_int { 100 } % Infinite (just needs to be greater than max_total_digits_int and max_display_divisions_int)
+\int_const:Nn \c__longdiv_max_total_digits_int { 60 }
+\int_const:Nn \c__longdiv_max_display_divisions_int { 20 }
+\int_new:N \l__longdiv_display_divisions_int
+
+% #1 -- remainder
+% #2 -- divisor
+% #3 -- rest of divits
+\cs_new:Nn \longdiv_get_new_digit:nnn {
+ \tl_if_empty:nTF { #3 } { % Are we out of digits?
+ % If we haven't hit the decimal point add it to the quotient and dividend
+ % Set seen_digit false so that we can remove the decimal point later if it divided evenly or we used \intlongdiv
+ \bool_if:NF \l__longdiv_seen_point_bool {
+ \longdiv_add_point: %
+ \tl_set:Nn \l__longdiv_extra_places_tl { . }
+ \bool_set_false:N \l__longdiv_seen_digit_bool
+ }
+ \longdiv_divide_no_more_digits:nn { #1 } { #2 }
+ }{
+ \longdiv_get_new_digit_aux:nnw { #1 } { #2 } #3;
+ }
+}
+\cs_generate_variant:Nn \longdiv_get_new_digit:nnn {xnn}
+
+\cs_new:Npn \longdiv_get_new_digit_aux:nnw #1 #2 #3 #4;{
+ \token_if_eq_meaning:NNTF #3 . {
+ \longdiv_add_point:
+ \bool_set_true:N \l__longdiv_seen_digit_bool % Prevent this decimal point from being removed later
+ \longdiv_get_new_digit:nnn { #1 } { #2 } { #4 }
+ }{
+ \longdiv_divide:nn { #1 #3 } { #2 } { #4 }
+ }
+}
+
+% Adds a decimal point, with a leading 0 if necessary, and records the current position in \l__longdiv_point_digit_int
+\cs_new:Nn \longdiv_add_point: {
+ \bool_set_true:N \l__longdiv_seen_point_bool
+ \bool_if:NTF \l__longdiv_seen_digit_bool {
+ \longdiv_linkedlist_add:n { . }
+ }{
+ \longdiv_linkedlist_add:n { 0. } % Add a leading zero
+
+ }
+ \int_set_eq:NN \l__longdiv_point_digit_int \l__longdiv_position_int % Record the position of the point
+}
+
+% Divide when we still have more digits.
+% #1 -- thing to divide
+% #2 -- divisor
+% Finds the quotient, adds it to the linked list and to the work token list then recurses.
+\cs_new:Nn \longdiv_divide:nn {
+ %\tl_show:n{#1}
+ \int_set:Nn \l__longdiv_quotient_int { \int_div_truncate:nn { #1 } { #2 } }
+ \bool_if:nF {
+ \int_compare_p:nNn \l__longdiv_quotient_int = \c_zero % If the quotient was zero, we might not have to print it
+ && !\l__longdiv_seen_digit_bool % If no other digits have been printed
+ && !\l__longdiv_seen_point_bool % And we are before the decimal point
+ }{ % Otherwise print it and record that we've seen a digit (all further 0's must be printed)
+ \bool_set_true:N \l__longdiv_seen_digit_bool
+ \longdiv_linkedlist_add:f { \int_use:N \l__longdiv_quotient_int }
+ }
+ \int_incr:N \l__longdiv_position_int
+ \longdiv_divide_record:nn{ #1 }{ #2 }
+ \longdiv_get_new_digit:xnn { \longdiv_remainder:nn { #1 } { #2 } } { #2 }
+}
+
+
+% Divide when we are out of digits.
+% #1 -- remainder from last time (we will add a zero to the end)
+% #2 -- divisor
+% This case is more complicated because we have to check for repeated remainders, and whether to stop
+% though we are certainly after the decimal point so we don't need to check whether we need to print 0's.
+\cs_new:Nn \longdiv_divide_no_more_digits:nn {
+ % If we've seen this remainder before, we're done. Use the appropriate command
+ % to insert the overline, and then typeset everything
+ \cs_if_exist_use:cTF { longdiv_remainders ~ \int_eval:n { #1 } }{ % \int_eval:n to remove leading zero
+ \longdiv_typeset:nnn { #1 } { #2 }
+ }{
+ \bool_if:nTF {
+ \int_compare_p:nNn \l__longdiv_extra_digits_int = \l__longdiv_max_extra_digits_int
+ ||\int_compare_p:nNn \l__longdiv_position_int = \c__longdiv_max_total_digits_int
+ }{
+ \int_compare:nNnT \l__longdiv_position_int = \c__longdiv_max_total_digits_int {
+ \msg_warning:nn { longdivision } { division_stopped_early }
+ }
+ \longdiv_typeset:nnn{#1}{#2}
+ }{
+ % Otherwise, record that we've seen this remainder and the position we're in
+ % In case this is the first digit of the repeated part
+ \cs_set:cpx { longdiv_remainders ~ \int_eval:n { #1 } }{ % \int_eval:n to remove leading zero
+ \exp_not:N \longdiv_linkedlist_put_overline:n { \int_use:N \l__longdiv_linkedlist_length_int }
+ }
+ % Now we have to use #10 everywhere
+ \int_set:Nn \l__longdiv_quotient_int { \int_div_truncate:nn { #10 } { #2 } }
+ \longdiv_linkedlist_add:f { \int_use:N \l__longdiv_quotient_int }
+ \bool_set_true:N \l__longdiv_seen_digit_bool % We've seen a digit after the decimal point, don't need to remove it
+ \int_incr:N \l__longdiv_position_int
+ \int_incr:N \l__longdiv_extra_digits_int
+ \tl_set:Nx \l__longdiv_extra_places_tl { \l__longdiv_extra_places_tl 0 } % Store an extra 0 appended to the dividend
+ \longdiv_divide_record:nn{#10}{#2}
+ \longdiv_divide_no_more_digits:xn { \longdiv_remainder:nn { #10 } { #2 } } { #2 }
+ }
+ }
+}
+\cs_generate_variant:Nn \longdiv_divide_no_more_digits:nn {xn}
+
+% Whenever we see the remainder 0, we're done, and we don't have to put an overline.
+\cs_set:cpn { longdiv_remainders ~ 0}{}
+
+% This command checks if the quotient was zero, and if so preserves the leading zero by avoiding \int_eval:n
+% This is so that e.g, \longdiv{14.1}{7} doesn't screw up
+\cs_new:Nn \longdiv_remainder:nn {
+ \int_compare:nNnTF \l__longdiv_quotient_int = \c_zero
+ { #1 }
+ { \int_eval:n { #1 - \l__longdiv_quotient_int * #2 } }
+}
+
+
+% We're going to store the "work" for the long division in this tl as a series of triples:
+% #1 -- number of digits we've processed so far (for positioning subtractions and determining if point should be added)
+% #2 -- old remainder (thing to subtract from)
+% #3 -- quotient * divisor (thing to subtract)
+\tl_new:N \l__longdiv_work_tl
+\cs_new:Nn \longdiv_divide_record:nn{
+ \int_compare:nNnTF \l__longdiv_display_divisions_int < \c__longdiv_max_display_divisions_int {
+ \int_compare:nNnF \l__longdiv_quotient_int = \c_zero { % If the quotient was zero, nothing needs to be typeset
+ \tl_set:Nx \l__longdiv_work_tl {
+ \l__longdiv_work_tl
+ { \int_use:N \l__longdiv_position_int } { #1 } { \int_eval:n { \l__longdiv_quotient_int * #2 } }
+ }
+ \int_incr:N \l__longdiv_display_divisions_int
+ }
+ }{
+ \int_compare:nNnT \l__longdiv_display_divisions_int = \c__longdiv_max_display_divisions_int {
+ \int_compare:nNnF \l__longdiv_quotient_int = \c_zero {
+ \tl_set:Nx \l__longdiv_work_tl {
+ \l__longdiv_work_tl
+ { \int_use:N \l__longdiv_position_int } { #1 } { \int_eval:n { \l__longdiv_quotient_int * #2 } }
+ \exp_not:N \longdiv_typeset_work_last:nn { \int_use:N \l__longdiv_position_int } { \int_eval:n { #1 - \l__longdiv_quotient_int * #2 } }
+ }
+ \int_incr:N \l__longdiv_display_divisions_int
+ \msg_warning:nn { longdivision } { work_stopped_early }
+ }
+ }
+ }
+}
+
+%%
+%% Typesetting
+%%
+
+
+% \l__longdiv_linkedlist_tl -- quotient
+% #1 -- remainder
+% #2 -- divisor
+% #3 -- dividend
+\cs_new:Nn \longdiv_typeset:nnn {
+ % If we haven't seen any new digits since adding a terminal decimal point, delete it.
+ \bool_if:NF \l__longdiv_seen_digit_bool { \longdiv_linkedlist_remove_tail: }
+
+ #2\,\begin{tabular}[b]{@{}r@{}}
+ \int_compare:nNnTF \l__longdiv_linkedlist_length_int = \c_zero
+ { 0 }
+ { \l__longdiv_linkedlist_tl }
+ \\\hline
+ \big)\begin{tabular}[t]{@{}l@{}}
+ % Likewise we don't want to add a trailing decimal point to the dividend if it divided evenly
+ #3 \bool_if:NT \l__longdiv_seen_digit_bool { \l__longdiv_extra_places_tl } \\
+ \longdiv_typeset_work:n { #1 }
+ \end{tabular}
+ \end{tabular}
+}
+
+% Iterate through the division "work" and typeset it
+\cs_new:Nn \longdiv_typeset_work:n {
+ \tl_if_empty:NTF \l__longdiv_work_tl {
+ \msg_warning:nn { longdivision } { no_division_occurred }
+ }{
+ \exp_after:wN \longdiv_typeset_work_first:nnn \l__longdiv_work_tl
+ \int_compare:nNnT \l__longdiv_display_divisions_int < \c__longdiv_max_display_divisions_int {
+ \exp_args:No \longdiv_typeset_work_last:nn { \int_use:N \l__longdiv_position_int } { #1 }
+ }
+ }
+}
+
+% #1 -- digits in to the right side of the numbers we are writing
+% #2 -- remainder from last time with new digits added to the right
+% #3 -- quotient * divisor
+% _first only typesets quotient * divisor and the line
+% _rest typesets result from last time, quotient * divisor and the line
+% _last only typesets the remainder from last time
+\cs_new:Nn \longdiv_typeset_work_first:nnn {
+ \longdiv_typeset_setwidth:n { #1 }
+ \hspace{\g__longdiv_temp_dim}
+ \llap { \longdiv_insert_point_ifneeded:nn { #1 } { #3 } }
+ \\\longdiv_rule:nn{#1}{#3}
+ \peek_meaning:NT \bgroup {
+ \longdiv_typeset_work_rest:nnn
+ }
+}
+
+\cs_new:Nn \longdiv_typeset_work_rest:nnn {
+ \longdiv_typeset_setwidth:n { #1 }
+ \hspace{\g__longdiv_temp_dim}
+ \llap { \longdiv_insert_point_ifneeded:nn { #1 } { #2 } }
+ \\
+ \hspace{\g__longdiv_temp_dim}
+ \llap { \longdiv_insert_point_ifneeded:nn { #1 } { #3 } }
+ \\\longdiv_rule:nn{#1}{#3}
+ \peek_meaning:NT \bgroup {
+ \longdiv_typeset_work_rest:nnn
+ }
+}
+
+% #1 -- digits in to the right side of the numbers we are writing
+% #2 -- remainder from last time with new digits added to the right
+\cs_new:Npn \longdiv_typeset_work_last:nn #1 #2 {
+ \longdiv_typeset_setwidth:n { #1 }
+ \hspace{\g__longdiv_temp_dim}
+ \llap { \longdiv_insert_point_ifneeded:nn { #1 } { #2 } }
+}
+
+% Set \g__longdiv_temp_dim equal to digitwidth * number of digits
+% If we are past the decimal point, add \c__longdiv_pointwidth_dim
+\cs_new:Nn \longdiv_typeset_setwidth:n {
+ \dim_gset:Nn \g__longdiv_temp_dim { #1\c__longdiv_digitwidth_dim }
+ \int_compare:nNnT \l__longdiv_point_digit_int < {#1} {
+ \dim_gadd:Nn \g__longdiv_temp_dim \c__longdiv_pointwidth_dim
+ }
+}
+
+% If the number ends after the decimal point ( #1 > \l__longdiv_point_digit_int )
+% and start before it ( #1 - length(#2) < \l__longdiv_point_digit_int) insert a
+% decimal point in the appropriate position of #2. Otherwise just return #2
+\cs_new:Nn \longdiv_insert_point_ifneeded:nn {
+ \bool_if:nTF {
+ \int_compare_p:nNn { #1 } > \l__longdiv_point_digit_int
+ && \int_compare_p:nNn { #1 - \tl_count:n { #2 } } < \l__longdiv_point_digit_int
+ }{
+ \longdiv_insert_point:nn {\int_eval:n{\l__longdiv_point_digit_int - #1 + \tl_count:n { #2 }}} { #2 }
+ }{
+ #2
+ }
+}
+
+% Walk #1 digits across #2 and then insert a decimal point.
+\cs_new:Nn \longdiv_insert_point:nn {
+ \longdiv_insert_point_aux:oN { #1 } #2
+}
+
+\cs_new:Nn \longdiv_insert_point_aux:nN {
+ \int_compare:nNnTF { #1 } = \c_zero {
+ .#2
+ }{
+ #2 \longdiv_insert_point_aux:oN { \int_eval:n { #1 - 1 } }
+ }
+}
+\cs_generate_variant:Nn \longdiv_insert_point_aux:nN {oN}
+
+
+% Okay, this is another section where we are adulterated with plaintex stuff.
+% It would be easy to reimplement \settowidth, but \hrule and \noalign have no
+% expl3 name anyways. Since I only use these dim variables with \settowidth, I declare
+% them with \newdimen rather than \dim_new:N
+\newdimen \c__longdiv_digitwidth_dim
+\settowidth \c__longdiv_digitwidth_dim { 0 }
+\newdimen \c__longdiv_pointwidth_dim
+\settowidth \c__longdiv_pointwidth_dim { . }
+\newdimen \l__longdiv_tempwidth_dim
+
+\cs_new:Nn \longdiv_rule:nn {
+ \noalign {
+ \settowidth \l__longdiv_tempwidth_dim { #2 }
+ % Check whether the decimal point occurred in the middle of the current number
+ % because if so, it's longer by pointwidth.
+ \bool_if:nT {
+ \int_compare_p:nNn { #1 } > \l__longdiv_point_digit_int
+ && \int_compare_p:nNn { #1 - \tl_count:n { #2 } } < \l__longdiv_point_digit_int
+ }{
+ \dim_add:Nn \l__longdiv_tempwidth_dim \c__longdiv_pointwidth_dim
+ }
+ \box_move_right:nn { \g__longdiv_temp_dim - \l__longdiv_tempwidth_dim } {
+ \vbox:n { \hrule width \l__longdiv_tempwidth_dim }
+ }
+ }
+}
+
+\ExplSyntaxOff