diff options
| author | Shulhan <ms@kilabit.info> | 2026-04-10 21:40:11 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2026-04-12 15:19:03 +0700 |
| commit | f9f699cd2bff3f9c6d84e597039f8bf694926792 (patch) | |
| tree | 1d52e9a35ab6e54aaa1167b74aa4fe77d98f8af8 | |
| parent | 44aa2ea2da7a02eb7d52d500e40fe4fd7b5752e3 (diff) | |
| download | pakakeh.go-f9f699cd2bff3f9c6d84e597039f8bf694926792.tar.xz | |
text/diff: implements unified diff
33 files changed, 1199 insertions, 22 deletions
diff --git a/lib/text/diff/diff.go b/lib/text/diff/diff.go index 947e13db..c587a829 100644 --- a/lib/text/diff/diff.go +++ b/lib/text/diff/diff.go @@ -19,10 +19,19 @@ var ( // Data stores additions, deletions, and changes between two texts. type Data struct { + oldlines []Line + newlines []Line + Adds []Line Dels []Line Changes LineChanges + // Unified contains the result of [Unified] diff, without the label + // ("@@ ... @@") and context lines. + // To print the result of unified diff with context lines use + // [Data.WriteUnified]. + Unified []Line + IsMatched bool } @@ -35,10 +44,12 @@ func Text(before, after []byte, level int) (diffs Data) { // Lines search the difference between two Lines. func Lines(oldlines, newlines []Line, level int) (diffs Data) { + diffs.oldlines = oldlines + diffs.newlines = newlines oldlen := len(oldlines) newlen := len(newlines) - x := 0 - y := 0 + x := 0 // cursor for old. + y := 0 // cursor for new. for x < oldlen { if y == newlen { @@ -52,6 +63,8 @@ func Lines(oldlines, newlines []Line, level int) (diffs Data) { // Compare old line with new line. if IsEqual(oldlines[x].Val, newlines[y].Val) { + oldlines[x].NumOther = newlines[y].Num + newlines[y].NumOther = oldlines[x].Num x++ y++ continue @@ -72,14 +85,14 @@ func Lines(oldlines, newlines []Line, level int) (diffs Data) { } // Old is empty or contain only whitespaces. - if oldtrimlen <= 0 { + if oldtrimlen == 0 { diffs.PushDel(oldlines[x]) x++ continue } // New is empty or contain only whitespaces. - if newtrimlen <= 0 { + if newtrimlen == 0 { diffs.PushAdd(newlines[y]) y++ continue @@ -142,8 +155,7 @@ func Lines(oldlines, newlines []Line, level int) (diffs Data) { case addlen == dellen: // Both changes occur between lines for x < yatx && y < xaty { - diffs.PushChange(oldlines[x], - newlines[y]) + diffs.PushChange(oldlines[x], newlines[y]) x++ y++ } @@ -191,19 +203,25 @@ func (diffs *Data) checkIsMatched() { // PushAdd will add new line to diff set. func (diffs *Data) PushAdd(new Line) { + new.Kind = LineKindAdd diffs.Adds = append(diffs.Adds, new) } // PushDel will add deletion line to diff set. func (diffs *Data) PushDel(old Line) { + old.Kind = LineKindDel diffs.Dels = append(diffs.Dels, old) } // PushChange set to diff data. func (diffs *Data) PushChange(old, new Line) { + if old.Num != new.Num { + old.NumOther, new.NumOther = new.Num, old.Num + } change := NewLineChange(old, new) - diffs.Changes = append(diffs.Changes, *change) + diffs.PushDel(old) + diffs.PushAdd(new) } // GetAllAdds return chunks of additions including in line changes. @@ -227,27 +245,17 @@ func (diffs *Data) GetAllDels() (chunks text.Chunks) { // String return formatted data. func (diffs Data) String() (s string) { var sb strings.Builder - if len(diffs.Dels) > 0 { sb.WriteString("----\n") for _, line := range diffs.Dels { - fmt.Fprintf(&sb, "%d - %q\n", line.Num, line.Val) + fmt.Fprintln(&sb, line.String()) } } - if len(diffs.Adds) > 0 { sb.WriteString("++++\n") for _, line := range diffs.Adds { - fmt.Fprintf(&sb, "%d + %q\n", line.Num, line.Val) + fmt.Fprintln(&sb, line.String()) } } - - if len(diffs.Changes) > 0 { - sb.WriteString("--++\n") - for _, change := range diffs.Changes { - sb.WriteString(change.String()) - } - } - return sb.String() } diff --git a/lib/text/diff/diff_test.go b/lib/text/diff/diff_test.go index 76dd174c..27cc7ad7 100644 --- a/lib/text/diff/diff_test.go +++ b/lib/text/diff/diff_test.go @@ -102,6 +102,7 @@ func TestBytesRatio(t *testing.T) { } func TestText(t *testing.T) { + t.Skip(`Unified output in progress`) type testCase struct { textBefore string textAfter string diff --git a/lib/text/diff/line.go b/lib/text/diff/line.go index 9fe3f878..e7a4825c 100644 --- a/lib/text/diff/line.go +++ b/lib/text/diff/line.go @@ -6,13 +6,24 @@ package diff import ( "bytes" "fmt" + "io" "os" ) +// List of kind of line in [Data]. +const ( + LineKindAdd = '+' + LineKindDel = '-' + LineKindLabel = '@' + LineKindNone = 0 +) + // Line represents single line from file or stream of bytes. type Line struct { - Val []byte // The line value. - Num int // The line number, started from 1. + Val []byte // The line value. + Kind int // The kind of changes. + Num int // The line number, started from 1. + NumOther int // The line number of other file. } // ParseLines returns lines from reading raw bytes. @@ -25,7 +36,8 @@ func ParseLines(raw []byte) (lines []Line) { } lines = make([]Line, 0, len(rawlines)) for x, line := range rawlines { - lines = append(lines, Line{Num: x + 1, Val: line}) + l := Line{Num: x + 1, NumOther: x + 1, Val: line} + lines = append(lines, l) } return lines } @@ -41,3 +53,13 @@ func ReadLines(file string) (lines []Line, err error) { lines = ParseLines(raw) return lines, nil } + +func WriteLines(w io.Writer, lines []Line) { + for _, line := range lines { + fmt.Fprintln(w, line.String()) + } +} + +func (line Line) String() string { + return fmt.Sprintf("%4d/%4d: %c%s", line.NumOther, line.Num, line.Kind, line.Val) +} diff --git a/lib/text/diff/testdata/List_of_United_Nations.reverse.uni b/lib/text/diff/testdata/List_of_United_Nations.reverse.uni new file mode 100644 index 00000000..c0645fce --- /dev/null +++ b/lib/text/diff/testdata/List_of_United_Nations.reverse.uni @@ -0,0 +1,31 @@ +--- old ++++ new +@@ -3,6 +3,10 @@ +
+ The [[United Nations General Assembly]] has adopted a number of resolutions saying that the strategic relationship with the United States encourages Israel to pursue aggressive and expansionist policies and practices.<ref>see General Assembly resolutions 36/226 A and B of 17 December 1981, ES-9/1 of 5 February 1982, 37/123 F of 20 December 1982 and 38/180 A to D of 19 December 1983, A/RES/39/146, 14 December 1984</ref> The 9th Emergency Session of the General Assembly was convened at the request of the Security Council when the United States blocked efforts to adopt sanctions against Israel.<ref>General Assembly resolution ES-9/1, 5 February 1982. The UN General Assembly approved a measure which 'Strongly deplores the negative vote by a permanent member of the Security Council which prevented the Council from adopting against Israel, under Chapter VII of the Charter, the "appropriate measures" referred to in resolution 497 (1981) unanimously adopted by the Council;'</ref>
+
++The [[United States]] has regularly voted alone and against international consensus, using its [[United Nations Security Council veto power|veto power]] to block the adoption of proposed UN Security Council resolutions supporting the [[PLO]] and calling for a two-state solution to the [[Israeli-Palestinian conflict]].<ref>[http://books.google.ca/books?id=CHL5SwGvobQC&pg=PA168&dq=US+veto+Israel+regularly#v=onepage&q=US%20veto%20Israel%20regularly&f=false Pirates and emperors, old and new: international terrorism in the real world], [[Noam Chomsky]], p. 168.</ref><ref>The US has also used its veto to block resolutions that are critical of Israel.[http://books.google.ca/books?id=yzmpDAz7ZAwC&pg=PT251&dq=US+veto+Israel+regularly&lr=#v=onepage&q=US%20veto%20Israel%20regularly&f=false Uneasy neighbors], David T. Jones and David Kilgour, p. 235.</ref> The United States responded to the frequent criticism from UN organs by adopting the [[Negroponte doctrine]].
++
++
++
+ == United Nations General Assembly resolutions ==
+ :''See also: [[United Nations General Assembly resolution]]''
+ {{expand list}}
+@@ -241,6 +245,9 @@ + # [[United Nations Security Council Resolution 1701|Resolution 1701]] (11 August 2006) called for the full cessation of hostilities between Israel and [[Hezbollah]].
+ # [[United Nations Security Council Resolution 1860|Resolution 1860]] (9 January 2009) called for the full cessation of war between Israel and [[Hamas]].
+
++==References==
++{{reflist}}
++
+ == See also ==
+ *[[United Nations]]
+ *[[Israel, Palestine and the United Nations]]
+@@ -265,7 +272,3 @@ +
+ [[ar:قائمة قرارات الأمم المتحدة المتعلقة بإسرائيل]]
+
+-==References==
+-{{reflist}}
+-
+- diff --git a/lib/text/diff/testdata/List_of_United_Nations.uni b/lib/text/diff/testdata/List_of_United_Nations.uni new file mode 100644 index 00000000..87b9327c --- /dev/null +++ b/lib/text/diff/testdata/List_of_United_Nations.uni @@ -0,0 +1,31 @@ +--- old ++++ new +@@ -3,10 +3,6 @@ +
+ The [[United Nations General Assembly]] has adopted a number of resolutions saying that the strategic relationship with the United States encourages Israel to pursue aggressive and expansionist policies and practices.<ref>see General Assembly resolutions 36/226 A and B of 17 December 1981, ES-9/1 of 5 February 1982, 37/123 F of 20 December 1982 and 38/180 A to D of 19 December 1983, A/RES/39/146, 14 December 1984</ref> The 9th Emergency Session of the General Assembly was convened at the request of the Security Council when the United States blocked efforts to adopt sanctions against Israel.<ref>General Assembly resolution ES-9/1, 5 February 1982. The UN General Assembly approved a measure which 'Strongly deplores the negative vote by a permanent member of the Security Council which prevented the Council from adopting against Israel, under Chapter VII of the Charter, the "appropriate measures" referred to in resolution 497 (1981) unanimously adopted by the Council;'</ref>
+
+-The [[United States]] has regularly voted alone and against international consensus, using its [[United Nations Security Council veto power|veto power]] to block the adoption of proposed UN Security Council resolutions supporting the [[PLO]] and calling for a two-state solution to the [[Israeli-Palestinian conflict]].<ref>[http://books.google.ca/books?id=CHL5SwGvobQC&pg=PA168&dq=US+veto+Israel+regularly#v=onepage&q=US%20veto%20Israel%20regularly&f=false Pirates and emperors, old and new: international terrorism in the real world], [[Noam Chomsky]], p. 168.</ref><ref>The US has also used its veto to block resolutions that are critical of Israel.[http://books.google.ca/books?id=yzmpDAz7ZAwC&pg=PT251&dq=US+veto+Israel+regularly&lr=#v=onepage&q=US%20veto%20Israel%20regularly&f=false Uneasy neighbors], David T. Jones and David Kilgour, p. 235.</ref> The United States responded to the frequent criticism from UN organs by adopting the [[Negroponte doctrine]].
+-
+-
+-
+ == United Nations General Assembly resolutions ==
+ :''See also: [[United Nations General Assembly resolution]]''
+ {{expand list}}
+@@ -245,9 +241,6 @@ + # [[United Nations Security Council Resolution 1701|Resolution 1701]] (11 August 2006) called for the full cessation of hostilities between Israel and [[Hezbollah]].
+ # [[United Nations Security Council Resolution 1860|Resolution 1860]] (9 January 2009) called for the full cessation of war between Israel and [[Hamas]].
+
+-==References==
+-{{reflist}}
+-
+ == See also ==
+ *[[United Nations]]
+ *[[Israel, Palestine and the United Nations]]
+@@ -272,3 +265,7 @@ +
+ [[ar:قائمة قرارات الأمم المتحدة المتعلقة بإسرائيل]]
+
++==References==
++{{reflist}}
++
++ diff --git a/lib/text/diff/testdata/Psusennes_II.reverse.uni b/lib/text/diff/testdata/Psusennes_II.reverse.uni new file mode 100644 index 00000000..8382813c --- /dev/null +++ b/lib/text/diff/testdata/Psusennes_II.reverse.uni @@ -0,0 +1,92 @@ +--- old ++++ new +@@ -1,57 +1,56 @@ +-{{Infobox pharaoh
+-| Name= Psusennes II
+-| Alt= Pasebakhaenniut II<ref>[http://www.digitalegypt.ucl.ac.uk/chronology/psusennesii.html Pasebakhenniut II]</ref>
+-| Image=
+-| NomenHiero= <hiero>M17-Y5:N35:U7-G40-N14*N28-N35:O49</hiero>
+-| Nomen=''Hor-Pasebakhaenniut''
+-| PrenomenHiero= <hiero>ra:D17-xpr-Z3-stp:n-ra</hiero>
+-|Prenomen=''Titkheperure/Tyetkheperre''
+-| Golden=
+-| Nebty=
+-| Horus=
+-| GoldenHiero=
+-| NebtyHiero=
+-| HorusHiero=
+-| Reign=967 – 943 BC
+-| Predecessor= [[Siamun]]
+-| Successor= [[Shoshenq I]]
+-| Spouse=
+-| Children= [[Maatkare (daughter of Psusennes II)|Maatkare]]
+-| Dynasty= [[Twenty-first dynasty of Egypt|21st Dynasty]]
+-| Father=
+-| Mother=
+-| Died= [[943 BC]]
+-| Burial= Unknown
+-| Monuments=
++{{Pharaoh Infobox |
++ Name= Psusennes II |
++ Alt= Pasebakhaenniut II<ref>[http://www.digitalegypt.ucl.ac.uk/chronology/psusennesii.html Pasebakhenniut II]</ref> |
++ Image= |
++ NomenHiero= <hiero>M17-Y5:N35:U7-G40-N14*N28-N35:O49</hiero>|
++ Nomen=''Hor-Pasebakhaenniut''|
++ PrenomenHiero= <hiero>ra:D17-xpr-Z3-stp:n-ra</hiero>|
++Prenomen=''Titkheperure/Tyetkheperre''|
++ Golden= |
++ Nebty= |
++ Horus= |
++ GoldenHiero= |
++ NebtyHiero= |
++ HorusHiero= |
++ Reign=967 – 943 BC |
++ Predecessor= [[Siamun]] |
++ Successor= [[Shoshenq I]] |
++ Spouse= |
++ Children= [[Maatkare (daughter of Psusennes II)|Maatkare]] |
++ Dynasty= [[Twenty-first dynasty of Egypt|21st Dynasty]] |
++ Father= |
++ Mother= |
++ Died= [[943 BC]] |
++ Burial= Unknown |
++ Monuments= |
+ }}
+
+ Titkheperure or Tyetkheperre '''Psusennes II''' <nowiki>[</nowiki>[[Greek language|Greek]] Ψουσέννης<nowiki>]</nowiki> or '''Hor-Pasebakhaenniut II''' <nowiki>[</nowiki>[[Egyptian language|Egyptian]] ''ḥr-p3-sb3-ḫˁỉ-<n>-nỉwt''<nowiki>]</nowiki>, was the last [[Pharaoh|king]] of the [[Twenty-first dynasty of Egypt]]. His royal name means "Image of the transformation of Re" in Egyptian.<ref>Peter Clayton, Chronology of the Pharaohs, Thames & Hudson Ltd, 1994. p.178</ref> Psusennes II is often considered the same person as the High-Priest of Amun known as [[Psusennes III]].<ref>Erik Hornung, Rolf Krauss & David Warburton (editors), Ancient Egyptian Chronology (Handbook of Oriental Studies), Brill: 2006, p.221 Karl Jansen-Winkeln in his treatment for the 'Dynasty 21' chapter of this book writes that "the evidence weighs heavily in favour of his (ie. Psusennes III) being one and the same man, who was first HP and then successor to King Siamun in Tanis, without giving up his Theban office."</ref> The Egyptologist Karl Jansen-Winkeln notes that an important graffito from the Temple of Abydos contains the complete titles of a king ''Tyetkheperre Setepenre Pasebakhaenniut Meryamun'' "who is simultaneously called the HPA (ie. High Priest of Amun) and supreme military commander."<ref>Jansen-Winkeln in Hornung, Krauss & Warburton, p.222</ref> This suggests that Psusennes was both king at Tanis and the High Priest in Thebes at the same time meaning he did not resign his office as High Priest of Amun during his reign.<ref>Jansen-Winkeln in Hornung, Krauss & Warburton, p.223</ref> The few contemporary attestations from his reign include the aforementioned [[Graffito (archaeology)|graffito]] in [[Seti I]]'s [[Abydos, Egypt|Abydos]] temple, an [[ostracon]] from [[Umm el-Qa'ab]], an affiliation at [[Karnak]] and his presumed burial – which consists of a gilded coffin with a royal [[Uraeus#Golden uraeus of Senusret II|uraeus]] and a [[Mummy]], found in an antechamber of [[Psusennes I]]'s tomb at [[Tanis, Egypt|Tanis]]. He was a [[High Priests of Amun at Thebes|High Priest of Amun at Thebes]] and the son of [[Pinedjem II]] and Istemkheb. His daughter [[Maatkare (daughter of Psusennes II)|Maatkare]] was the [[Great Royal Wife]] of [[Osorkon I]].
+
+-Items which can be added to this list include a Year 5 Mummy linen that was written with the High Priest Psusennes III's name. It is generally assumed that a '''Year 13 III Peret 10+X''' date in fragment 3B, line 6 of the Karnak Priestly Annals belongs to his reign.<ref>K.A. Kitchen, The Third Intermediate Period in Egypt (1100–650 BC) 3rd ed., Warminster: Aris & Phillips Ltd, p.423</ref> Unfortunately, however, the king's name is not stated and the only thing which is certain is that the fragment must be dated after Siamun's reign whose Year 17 is mentioned in lines 3-5.<ref name="Kitchen, p.423">Kitchen, p.423</ref> Hence, it belongs to either Psusennes II or possibly Shoshenq I's reign. More impressive are the number of objects which associate Psusennes II together with his successor, Shoshenq I, such as an old statue of [[Thutmose III]] which contains two parallel columns of texts – one referring to Psusennes II and the other to [[Shoshenq I]] – a recently unearthed block from [[Basta]] which preserves the nomen of Shoshenq I together with the prenomen of Psusennes II, and a now lost graffito from [[TT18|Theban Tomb 18]].<ref>Aidan Dodson, "Psusennes II and Shoshenq I," JEA 79(1993), pp.267-268</ref>
++Items which can be added to this list include a Year 5 Mummy linen that was written with the High Priest Psusennes III's name. It is generally assumed that a '''Year 13 III Peret 10+X''' date in fragment 3B, line 6 of the Karnak Priestly Annals belongs to his reign.<ref>K.A. Kitchen, The Third Intermediate Period in Egypt (1100–650 BC) 3rd ed., Warminster: Aris & Phillips Ltd, p.423</ref> Unfortunately, however, the king's name is not stated and the only thing which is certain is that the fragment must be dated after Siamun's reign whose Year 17 is mentioned in lines 3-5.<ref>Kitchen, p.423</ref> Hence, it belongs to either Psusennes II or possibly Shoshenq I's reign. More impressive are the number of objects which associate Psusennes II together with his successor, Shoshenq I, such as an old statue of [[Thutmose III]] which contains two parallel columns of texts – one referring to Psusennes II and the other to [[Shoshenq I]] – a recently unearthed block from [[Basta]] which preserves the nomen of Shoshenq I together with the prenomen of Psusennes II, and a now lost graffito from [[TT18|Theban Tomb 18]].<ref>Aidan Dodson, "Psusennes II and Shoshenq I," JEA 79(1993), pp.267-268</ref>
+
+-Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref name="Payraudeau, BIFAO 108, p.294">Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records—in the following line—the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref name="Payraudeau, BIFAO 108, p.294"/> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign.
++Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref>Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records--in the following line--the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref>Payraudeau, BIFAO 108, p.294</ref> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign.
+
+ ==Reign Length==
+ Unlike his immediate predecessor and successor – [[Siamun]] and [[Shoshenq I]] respectively– Psusennes II is generally less well attested in contemporary historical records even though various versions of [[Manetho]]'s [[Epitome]] credits him with either a 14 or a 35 year reign, (generally amended to 15 years by most scholars including the British Egyptologist [[Kenneth Kitchen]]).<ref>K.A. Kitchen, The Third Intermediate Period in Egypt (c.1100-650 BC), Aris & Phillips Ltd., 1996. p.531</ref> However, the German scholar Rolf Krauss has recently argued that Psusennes II's reign was 24 years rather than Manetho's original figure of 14 years.<ref>Rolf Krauss, Das wrŝ-Datum aus Jahr 5 von Shoshenq [I], Discussions in Egyptology 62 (2005), pp.43-48</ref> This is based on personal information recorded in the Large Dakhla stela which dates to Year 5 of Shoshenq I; the stela preserves a reference to a land-register from ''Year 19 of a 'Pharaoh Psusennes'.''
+
+-In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref name="Kitchen, p.290">Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref name="Kitchen, p.290"/> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14–15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I—a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead—Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years.
++In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref>Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref>Kitchen, p.290</ref> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14-15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I--a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead--Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years.
+
+-The term "mother" in ancient Egypt could also be an allusion to an ancestress, the matriarch of a lineage whereby Nysu-Bastet may have been petitioning for his hereditary water rights that belonged to his grandmother, whose family name was Tewhunet. However, this argument does not account for the use of Pharaoh as a title in the Dakhla stela—a literary device which first occurs late during the reign of Siamun, an Egyptian king who ruled between 45 to 64 years after Year 19 of Psusennes I.
++The term "mother" in ancient Egypt could also be an allusion to an ancestress, the matriarch of a lineage whereby Nysu-Bastet may have been petitioning for his hereditary water rights that belonged to his grandmother, whose family name was Tewhunet. However, this argument does not account for the use of Pharaoh as a title in the Dakhla stela--a literary device which first occurs late during the reign of Siamun, an Egyptian king who ruled between 45 to 64 years after Year 19 of Psusennes I.
+
+-The most significant component of the Great Dakhla stela is its palaeography: the use of the title Pharaoh Psusennes. A scholar named Helen Jacquet-Gordon believed in the 1970s that the large Dakhla stela belonged to [[Shoshenq III]]'s reign due to its use of the title 'Pharaoh' directly with the ruling king's birth name—ie: "'''Pharaoh Shoshenq'''"--which was an important palaeographical development in Egyptian history. Throughout the Old, Middle and New Kingdoms of Ancient Egypt, the word pharaoh was never employed as a title such as Mr. and Mrs. or attached to a king's [[Ancient Egyptian royal titulary#Personal name (nomen)|nomen]] such as ''Pharaoh Ramesses'' or ''Pharaoh Amenhotep''; instead, the word '''pr-`3''' or pharaoh was used as a noun to refer to the ''activities'' of the king (i.e., it was "Pharaoh" who ordered the creation of a temple or statue, or the digging of a well, etc.). Rolf Krauss aptly observes that the earliest attested use of the word pharaoh as a title is documented in Year 17 of the 21st Dynasty king [[Siamun]] from Karnak Priestly Annals fragment 3B<ref>J-M Kruchten, Les annales des prētres de Karnak (OLA) 1989. pp.47-48</ref> while a second use of the title ''''[Pharaoh] [birth name]'''' occurs during Psusennes II's reign where a hieratic graffito in the Ptah chapel of the Abydos temple of Seti I explicitly refers to Psusennes II as the "High Priest of Amen-Re, King of the Gods, the Leader, '''Pharaoh Psusennes'''."<ref>M.A. Murray, The Osireion at Abydos (London, 1989), 36; pl. XXI</ref><ref>Krauss, DE 62, pp.43-44</ref> Consequently, the practice of attaching the title ''pr-`3'' or pharaoh with a king's royal birth name had already started prior to the beginning of Shoshenq I's reign, let alone Shoshenq III. Hence, the Shoshenq mentioned in the large Year 5 Dakhla stela must have been Shoshenq I while the Psusennes mentioned in the same document likewise can only be Psusennes II which means that only 5 years (or 10 years if Psusennes II ruled Egypt for 24 years) would separate Nysu-Bastet from his mother.<ref name="Krauss, DE 62, pp.43-48">Krauss, DE 62, pp.43-48</ref> The additional fact that the Large Dakhla stela contains a Year 5 IV Peret day 25 lunar date has helped date the aforementioned king Shoshenq's accession to 943 BC and demonstrates that the ruler here must be Shoshenq I, not Shoshenq III who ruled a century later.<ref name="Krauss, DE 62, pp.43-48"/> Helen Jacquet-Gordon did not know of the two prior examples pertaining to Siamun and Psusennes II.
++The most significant component of the Great Dakhla stela is its palaeography: the use of the title Pharaoh Psusennes. A scholar named Helen Jacquet-Gordon believed in the 1970s that the large Dakhla stela belonged to [[Shoshenq III]]'s reign due to its use of the title 'Pharaoh' directly with the ruling king's birth name--ie: "'''Pharaoh Shoshenq'''"--which was an important palaeographical development in Egyptian history. Throughout the Old, Middle and New Kingdoms of Ancient Egypt, the word pharaoh was never employed as a title such as Mr. and Mrs. or attached to a king's [[Ancient Egyptian royal titulary#Personal name (nomen)|nomen]] such as ''Pharaoh Ramesses'' or ''Pharaoh Amenhotep''; instead, the word '''pr-`3''' or pharaoh was used as a noun to refer to the ''activities'' of the king (i.e., it was "Pharaoh" who ordered the creation of a temple or statue, or the digging of a well, etc.). Rolf Krauss aptly observes that the earliest attested use of the word pharaoh as a title is documented in Year 17 of the 21st Dynasty king [[Siamun]] from Karnak Priestly Annals fragment 3B<ref>J-M Kruchten, Les annales des prētres de Karnak (OLA) 1989. pp.47-48</ref> while a second use of the title ''''[Pharaoh] [birth name]'''' occurs during Psusennes II's reign where a hieratic graffito in the Ptah chapel of the Abydos temple of Seti I explicitly refers to Psusennes II as the "High Priest of Amen-Re, King of the Gods, the Leader, '''Pharaoh Psusennes'''."<ref>M.A. Murray, The Osireion at Abydos (London, 1989), 36; pl. XXI</ref><ref>Krauss, DE 62, pp.43-44</ref> Consequently, the practice of attaching the title ''pr-`3'' or pharaoh with a king's royal birth name had already started prior to the beginning of Shoshenq I's reign, let alone Shoshenq III. Hence, the Shoshenq mentioned in the large Year 5 Dakhla stela must have been Shoshenq I while the Psusennes mentioned in the same document likewise can only be Psusennes II which means that only 5 years (or 10 years if Psusennes II ruled Egypt for 24 years) would separate Nysu-Bastet from his mother.<ref>Krauss, DE 62, pp.43-48</ref> The additional fact that the Large Dakhla stela contains a Year 5 IV Peret day 25 lunar date has helped date the aforementioned king Shoshenq's accession to 943 BC and demonstrates that the ruler here must be Shoshenq I, not Shoshenq III who ruled a century later.<ref>Krauss, DE 62, pp.43-48</ref> Helen Jacquet-Gordon did not know of the two prior examples pertaining to Siamun and Psusennes II.
+
+ ==Timeline==
+-The editors of the recent 2006 book on titled 'Handbook on Ancient Egyptian Chronology'--Erik Hornung, Rolf Krauss and David Warburton—accept this logical reasoning and have amended Manetho's original figure of 14 years for Psusennes II to 24 years instead to Psusennes II.<ref>Erik Hornung, Rolf Krauss & David Warburton (editors), Handbook of Ancient Egyptian Chronology (Handbook of Oriental Studies), Brill: 2006, p.474 & p.488</ref> This is not unprecedented since previous Egyptologists had previously amended the reign of Siamun by a decade from 9 years—as preserved in surviving copies of Manetho's Epitome—to 19 years based on certain Year 16 and Year 17 dates attested for the latter.<ref name="Kitchen, p.423"/> Psusennes II ruled Egypt for a minimum of 19 years based on the internal chronology of the Large Dakhla stela. However, a calculation of a lunar ''Tepi Shemu'' feast which records the induction of Hori son of Nespaneferhor into the Amun priesthood in regnal year 17 of [[Siamun]], Psusennes II's predecessor—demonstrates that this date was equivalent to 970 BC.<ref>Hornung, Krauss & Warburton, pp.474-475</ref> Since Siamun enjoyed a reign of 19 years, he would have died 2 years later in 968/967 BC and been succeeded by Psusennes II by 967 BC at the latest. Consequently, a reign of 24 years or 967-943 BC is now likely for Psusennes II; hence, his reign has been raised from 14 to 24 years.
++The editors of the recent 2006 book on titled 'Handbook on Ancient Egyptian Chronology'--Erik Hornung, Rolf Krauss and David Warburton--accept this logical reasoning and have amended Manetho's original figure of 14 years for Psusennes II to 24 years instead to Psusennes II.<ref>Erik Hornung, Rolf Krauss & David Warburton (editors), Handbook of Ancient Egyptian Chronology (Handbook of Oriental Studies), Brill: 2006, p.474 & p.488</ref> This is not unprecedented since previous Egyptologists had previously amended the reign of Siamun by a decade from 9 years--as preserved in surviving copies of Manetho's Epitome--to 19 years based on certain Year 16 and Year 17 dates attested for the latter.<ref>Kitchen, p.423</ref> Psusennes II ruled Egypt for a minimum of 19 years based on the internal chronology of the Large Dakhla stela. However, a calculation of a lunar ''Tepi Shemu'' feast which records the induction of Hori son of Nespaneferhor into the Amun priesthood in regnal year 17 of [[Siamun]], Psusennes II's predecessor--demonstrates that this date was equivalent to 970 BC.<ref>Hornung, Krauss & Warburton, pp.474-475</ref> Since Siamun enjoyed a reign of 19 years, he would have died 2 years later in 968/967 BC and been succeeded by Psusennes II by 967 BC at the latest. Consequently, a reign of 24 years or 967-943 BC is now likely for Psusennes II; hence, his reign has been raised from 14 to 24 years.
+
+ Psusennes II's royal name has been found associated with his successor, [[Shoshenq I]] in a [[Graffito (archaeology)|graffito]] from tomb [[TT18]], and in an [[ostracon]] from [[Umm el-Qa'ab]].<ref>Aidan Dodson, "Psusennes II and Shoshenq I," [[Journal of Egyptian Archaeology|JEA]] 79(1993), pp.267-268</ref>
+
+ ==References==
+ {{reflist}}
+
+-* Aidan Dodson, RdE 38(1987), pp. 50-51.
++* Aidan Dodson, RdE 38(1987), pp.50-51.
+ * Jean Yoyotte, "A propos de Psousennes II," BSSFT 1(1988).
+
+-{{DEFAULTSORT:Psusennes Ii}}
+ [[Category:Pharaohs of the Twenty-first dynasty of Egypt]]
+
+ [[cs:Pasbachaenniut II.]]
diff --git a/lib/text/diff/testdata/Psusennes_II.uni b/lib/text/diff/testdata/Psusennes_II.uni new file mode 100644 index 00000000..cefc66a8 --- /dev/null +++ b/lib/text/diff/testdata/Psusennes_II.uni @@ -0,0 +1,92 @@ +--- old ++++ new +@@ -1,56 +1,57 @@ +-{{Pharaoh Infobox |
+- Name= Psusennes II |
+- Alt= Pasebakhaenniut II<ref>[http://www.digitalegypt.ucl.ac.uk/chronology/psusennesii.html Pasebakhenniut II]</ref> |
+- Image= |
+- NomenHiero= <hiero>M17-Y5:N35:U7-G40-N14*N28-N35:O49</hiero>|
+- Nomen=''Hor-Pasebakhaenniut''|
+- PrenomenHiero= <hiero>ra:D17-xpr-Z3-stp:n-ra</hiero>|
+-Prenomen=''Titkheperure/Tyetkheperre''|
+- Golden= |
+- Nebty= |
+- Horus= |
+- GoldenHiero= |
+- NebtyHiero= |
+- HorusHiero= |
+- Reign=967 – 943 BC |
+- Predecessor= [[Siamun]] |
+- Successor= [[Shoshenq I]] |
+- Spouse= |
+- Children= [[Maatkare (daughter of Psusennes II)|Maatkare]] |
+- Dynasty= [[Twenty-first dynasty of Egypt|21st Dynasty]] |
+- Father= |
+- Mother= |
+- Died= [[943 BC]] |
+- Burial= Unknown |
+- Monuments= |
++{{Infobox pharaoh
++| Name= Psusennes II
++| Alt= Pasebakhaenniut II<ref>[http://www.digitalegypt.ucl.ac.uk/chronology/psusennesii.html Pasebakhenniut II]</ref>
++| Image=
++| NomenHiero= <hiero>M17-Y5:N35:U7-G40-N14*N28-N35:O49</hiero>
++| Nomen=''Hor-Pasebakhaenniut''
++| PrenomenHiero= <hiero>ra:D17-xpr-Z3-stp:n-ra</hiero>
++|Prenomen=''Titkheperure/Tyetkheperre''
++| Golden=
++| Nebty=
++| Horus=
++| GoldenHiero=
++| NebtyHiero=
++| HorusHiero=
++| Reign=967 – 943 BC
++| Predecessor= [[Siamun]]
++| Successor= [[Shoshenq I]]
++| Spouse=
++| Children= [[Maatkare (daughter of Psusennes II)|Maatkare]]
++| Dynasty= [[Twenty-first dynasty of Egypt|21st Dynasty]]
++| Father=
++| Mother=
++| Died= [[943 BC]]
++| Burial= Unknown
++| Monuments=
+ }}
+
+ Titkheperure or Tyetkheperre '''Psusennes II''' <nowiki>[</nowiki>[[Greek language|Greek]] Ψουσέννης<nowiki>]</nowiki> or '''Hor-Pasebakhaenniut II''' <nowiki>[</nowiki>[[Egyptian language|Egyptian]] ''ḥr-p3-sb3-ḫˁỉ-<n>-nỉwt''<nowiki>]</nowiki>, was the last [[Pharaoh|king]] of the [[Twenty-first dynasty of Egypt]]. His royal name means "Image of the transformation of Re" in Egyptian.<ref>Peter Clayton, Chronology of the Pharaohs, Thames & Hudson Ltd, 1994. p.178</ref> Psusennes II is often considered the same person as the High-Priest of Amun known as [[Psusennes III]].<ref>Erik Hornung, Rolf Krauss & David Warburton (editors), Ancient Egyptian Chronology (Handbook of Oriental Studies), Brill: 2006, p.221 Karl Jansen-Winkeln in his treatment for the 'Dynasty 21' chapter of this book writes that "the evidence weighs heavily in favour of his (ie. Psusennes III) being one and the same man, who was first HP and then successor to King Siamun in Tanis, without giving up his Theban office."</ref> The Egyptologist Karl Jansen-Winkeln notes that an important graffito from the Temple of Abydos contains the complete titles of a king ''Tyetkheperre Setepenre Pasebakhaenniut Meryamun'' "who is simultaneously called the HPA (ie. High Priest of Amun) and supreme military commander."<ref>Jansen-Winkeln in Hornung, Krauss & Warburton, p.222</ref> This suggests that Psusennes was both king at Tanis and the High Priest in Thebes at the same time meaning he did not resign his office as High Priest of Amun during his reign.<ref>Jansen-Winkeln in Hornung, Krauss & Warburton, p.223</ref> The few contemporary attestations from his reign include the aforementioned [[Graffito (archaeology)|graffito]] in [[Seti I]]'s [[Abydos, Egypt|Abydos]] temple, an [[ostracon]] from [[Umm el-Qa'ab]], an affiliation at [[Karnak]] and his presumed burial – which consists of a gilded coffin with a royal [[Uraeus#Golden uraeus of Senusret II|uraeus]] and a [[Mummy]], found in an antechamber of [[Psusennes I]]'s tomb at [[Tanis, Egypt|Tanis]]. He was a [[High Priests of Amun at Thebes|High Priest of Amun at Thebes]] and the son of [[Pinedjem II]] and Istemkheb. His daughter [[Maatkare (daughter of Psusennes II)|Maatkare]] was the [[Great Royal Wife]] of [[Osorkon I]].
+
+-Items which can be added to this list include a Year 5 Mummy linen that was written with the High Priest Psusennes III's name. It is generally assumed that a '''Year 13 III Peret 10+X''' date in fragment 3B, line 6 of the Karnak Priestly Annals belongs to his reign.<ref>K.A. Kitchen, The Third Intermediate Period in Egypt (1100–650 BC) 3rd ed., Warminster: Aris & Phillips Ltd, p.423</ref> Unfortunately, however, the king's name is not stated and the only thing which is certain is that the fragment must be dated after Siamun's reign whose Year 17 is mentioned in lines 3-5.<ref>Kitchen, p.423</ref> Hence, it belongs to either Psusennes II or possibly Shoshenq I's reign. More impressive are the number of objects which associate Psusennes II together with his successor, Shoshenq I, such as an old statue of [[Thutmose III]] which contains two parallel columns of texts – one referring to Psusennes II and the other to [[Shoshenq I]] – a recently unearthed block from [[Basta]] which preserves the nomen of Shoshenq I together with the prenomen of Psusennes II, and a now lost graffito from [[TT18|Theban Tomb 18]].<ref>Aidan Dodson, "Psusennes II and Shoshenq I," JEA 79(1993), pp.267-268</ref>
++Items which can be added to this list include a Year 5 Mummy linen that was written with the High Priest Psusennes III's name. It is generally assumed that a '''Year 13 III Peret 10+X''' date in fragment 3B, line 6 of the Karnak Priestly Annals belongs to his reign.<ref>K.A. Kitchen, The Third Intermediate Period in Egypt (1100–650 BC) 3rd ed., Warminster: Aris & Phillips Ltd, p.423</ref> Unfortunately, however, the king's name is not stated and the only thing which is certain is that the fragment must be dated after Siamun's reign whose Year 17 is mentioned in lines 3-5.<ref name="Kitchen, p.423">Kitchen, p.423</ref> Hence, it belongs to either Psusennes II or possibly Shoshenq I's reign. More impressive are the number of objects which associate Psusennes II together with his successor, Shoshenq I, such as an old statue of [[Thutmose III]] which contains two parallel columns of texts – one referring to Psusennes II and the other to [[Shoshenq I]] – a recently unearthed block from [[Basta]] which preserves the nomen of Shoshenq I together with the prenomen of Psusennes II, and a now lost graffito from [[TT18|Theban Tomb 18]].<ref>Aidan Dodson, "Psusennes II and Shoshenq I," JEA 79(1993), pp.267-268</ref>
+
+-Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref>Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records--in the following line--the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref>Payraudeau, BIFAO 108, p.294</ref> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign.
++Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref name="Payraudeau, BIFAO 108, p.294">Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records—in the following line—the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref name="Payraudeau, BIFAO 108, p.294"/> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign.
+
+ ==Reign Length==
+ Unlike his immediate predecessor and successor – [[Siamun]] and [[Shoshenq I]] respectively– Psusennes II is generally less well attested in contemporary historical records even though various versions of [[Manetho]]'s [[Epitome]] credits him with either a 14 or a 35 year reign, (generally amended to 15 years by most scholars including the British Egyptologist [[Kenneth Kitchen]]).<ref>K.A. Kitchen, The Third Intermediate Period in Egypt (c.1100-650 BC), Aris & Phillips Ltd., 1996. p.531</ref> However, the German scholar Rolf Krauss has recently argued that Psusennes II's reign was 24 years rather than Manetho's original figure of 14 years.<ref>Rolf Krauss, Das wrŝ-Datum aus Jahr 5 von Shoshenq [I], Discussions in Egyptology 62 (2005), pp.43-48</ref> This is based on personal information recorded in the Large Dakhla stela which dates to Year 5 of Shoshenq I; the stela preserves a reference to a land-register from ''Year 19 of a 'Pharaoh Psusennes'.''
+
+-In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref>Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref>Kitchen, p.290</ref> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14-15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I--a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead--Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years.
++In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref name="Kitchen, p.290">Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref name="Kitchen, p.290"/> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14–15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I—a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead—Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years.
+
+-The term "mother" in ancient Egypt could also be an allusion to an ancestress, the matriarch of a lineage whereby Nysu-Bastet may have been petitioning for his hereditary water rights that belonged to his grandmother, whose family name was Tewhunet. However, this argument does not account for the use of Pharaoh as a title in the Dakhla stela--a literary device which first occurs late during the reign of Siamun, an Egyptian king who ruled between 45 to 64 years after Year 19 of Psusennes I.
++The term "mother" in ancient Egypt could also be an allusion to an ancestress, the matriarch of a lineage whereby Nysu-Bastet may have been petitioning for his hereditary water rights that belonged to his grandmother, whose family name was Tewhunet. However, this argument does not account for the use of Pharaoh as a title in the Dakhla stela—a literary device which first occurs late during the reign of Siamun, an Egyptian king who ruled between 45 to 64 years after Year 19 of Psusennes I.
+
+-The most significant component of the Great Dakhla stela is its palaeography: the use of the title Pharaoh Psusennes. A scholar named Helen Jacquet-Gordon believed in the 1970s that the large Dakhla stela belonged to [[Shoshenq III]]'s reign due to its use of the title 'Pharaoh' directly with the ruling king's birth name--ie: "'''Pharaoh Shoshenq'''"--which was an important palaeographical development in Egyptian history. Throughout the Old, Middle and New Kingdoms of Ancient Egypt, the word pharaoh was never employed as a title such as Mr. and Mrs. or attached to a king's [[Ancient Egyptian royal titulary#Personal name (nomen)|nomen]] such as ''Pharaoh Ramesses'' or ''Pharaoh Amenhotep''; instead, the word '''pr-`3''' or pharaoh was used as a noun to refer to the ''activities'' of the king (i.e., it was "Pharaoh" who ordered the creation of a temple or statue, or the digging of a well, etc.). Rolf Krauss aptly observes that the earliest attested use of the word pharaoh as a title is documented in Year 17 of the 21st Dynasty king [[Siamun]] from Karnak Priestly Annals fragment 3B<ref>J-M Kruchten, Les annales des prētres de Karnak (OLA) 1989. pp.47-48</ref> while a second use of the title ''''[Pharaoh] [birth name]'''' occurs during Psusennes II's reign where a hieratic graffito in the Ptah chapel of the Abydos temple of Seti I explicitly refers to Psusennes II as the "High Priest of Amen-Re, King of the Gods, the Leader, '''Pharaoh Psusennes'''."<ref>M.A. Murray, The Osireion at Abydos (London, 1989), 36; pl. XXI</ref><ref>Krauss, DE 62, pp.43-44</ref> Consequently, the practice of attaching the title ''pr-`3'' or pharaoh with a king's royal birth name had already started prior to the beginning of Shoshenq I's reign, let alone Shoshenq III. Hence, the Shoshenq mentioned in the large Year 5 Dakhla stela must have been Shoshenq I while the Psusennes mentioned in the same document likewise can only be Psusennes II which means that only 5 years (or 10 years if Psusennes II ruled Egypt for 24 years) would separate Nysu-Bastet from his mother.<ref>Krauss, DE 62, pp.43-48</ref> The additional fact that the Large Dakhla stela contains a Year 5 IV Peret day 25 lunar date has helped date the aforementioned king Shoshenq's accession to 943 BC and demonstrates that the ruler here must be Shoshenq I, not Shoshenq III who ruled a century later.<ref>Krauss, DE 62, pp.43-48</ref> Helen Jacquet-Gordon did not know of the two prior examples pertaining to Siamun and Psusennes II.
++The most significant component of the Great Dakhla stela is its palaeography: the use of the title Pharaoh Psusennes. A scholar named Helen Jacquet-Gordon believed in the 1970s that the large Dakhla stela belonged to [[Shoshenq III]]'s reign due to its use of the title 'Pharaoh' directly with the ruling king's birth name—ie: "'''Pharaoh Shoshenq'''"--which was an important palaeographical development in Egyptian history. Throughout the Old, Middle and New Kingdoms of Ancient Egypt, the word pharaoh was never employed as a title such as Mr. and Mrs. or attached to a king's [[Ancient Egyptian royal titulary#Personal name (nomen)|nomen]] such as ''Pharaoh Ramesses'' or ''Pharaoh Amenhotep''; instead, the word '''pr-`3''' or pharaoh was used as a noun to refer to the ''activities'' of the king (i.e., it was "Pharaoh" who ordered the creation of a temple or statue, or the digging of a well, etc.). Rolf Krauss aptly observes that the earliest attested use of the word pharaoh as a title is documented in Year 17 of the 21st Dynasty king [[Siamun]] from Karnak Priestly Annals fragment 3B<ref>J-M Kruchten, Les annales des prētres de Karnak (OLA) 1989. pp.47-48</ref> while a second use of the title ''''[Pharaoh] [birth name]'''' occurs during Psusennes II's reign where a hieratic graffito in the Ptah chapel of the Abydos temple of Seti I explicitly refers to Psusennes II as the "High Priest of Amen-Re, King of the Gods, the Leader, '''Pharaoh Psusennes'''."<ref>M.A. Murray, The Osireion at Abydos (London, 1989), 36; pl. XXI</ref><ref>Krauss, DE 62, pp.43-44</ref> Consequently, the practice of attaching the title ''pr-`3'' or pharaoh with a king's royal birth name had already started prior to the beginning of Shoshenq I's reign, let alone Shoshenq III. Hence, the Shoshenq mentioned in the large Year 5 Dakhla stela must have been Shoshenq I while the Psusennes mentioned in the same document likewise can only be Psusennes II which means that only 5 years (or 10 years if Psusennes II ruled Egypt for 24 years) would separate Nysu-Bastet from his mother.<ref name="Krauss, DE 62, pp.43-48">Krauss, DE 62, pp.43-48</ref> The additional fact that the Large Dakhla stela contains a Year 5 IV Peret day 25 lunar date has helped date the aforementioned king Shoshenq's accession to 943 BC and demonstrates that the ruler here must be Shoshenq I, not Shoshenq III who ruled a century later.<ref name="Krauss, DE 62, pp.43-48"/> Helen Jacquet-Gordon did not know of the two prior examples pertaining to Siamun and Psusennes II.
+
+ ==Timeline==
+-The editors of the recent 2006 book on titled 'Handbook on Ancient Egyptian Chronology'--Erik Hornung, Rolf Krauss and David Warburton--accept this logical reasoning and have amended Manetho's original figure of 14 years for Psusennes II to 24 years instead to Psusennes II.<ref>Erik Hornung, Rolf Krauss & David Warburton (editors), Handbook of Ancient Egyptian Chronology (Handbook of Oriental Studies), Brill: 2006, p.474 & p.488</ref> This is not unprecedented since previous Egyptologists had previously amended the reign of Siamun by a decade from 9 years--as preserved in surviving copies of Manetho's Epitome--to 19 years based on certain Year 16 and Year 17 dates attested for the latter.<ref>Kitchen, p.423</ref> Psusennes II ruled Egypt for a minimum of 19 years based on the internal chronology of the Large Dakhla stela. However, a calculation of a lunar ''Tepi Shemu'' feast which records the induction of Hori son of Nespaneferhor into the Amun priesthood in regnal year 17 of [[Siamun]], Psusennes II's predecessor--demonstrates that this date was equivalent to 970 BC.<ref>Hornung, Krauss & Warburton, pp.474-475</ref> Since Siamun enjoyed a reign of 19 years, he would have died 2 years later in 968/967 BC and been succeeded by Psusennes II by 967 BC at the latest. Consequently, a reign of 24 years or 967-943 BC is now likely for Psusennes II; hence, his reign has been raised from 14 to 24 years.
++The editors of the recent 2006 book on titled 'Handbook on Ancient Egyptian Chronology'--Erik Hornung, Rolf Krauss and David Warburton—accept this logical reasoning and have amended Manetho's original figure of 14 years for Psusennes II to 24 years instead to Psusennes II.<ref>Erik Hornung, Rolf Krauss & David Warburton (editors), Handbook of Ancient Egyptian Chronology (Handbook of Oriental Studies), Brill: 2006, p.474 & p.488</ref> This is not unprecedented since previous Egyptologists had previously amended the reign of Siamun by a decade from 9 years—as preserved in surviving copies of Manetho's Epitome—to 19 years based on certain Year 16 and Year 17 dates attested for the latter.<ref name="Kitchen, p.423"/> Psusennes II ruled Egypt for a minimum of 19 years based on the internal chronology of the Large Dakhla stela. However, a calculation of a lunar ''Tepi Shemu'' feast which records the induction of Hori son of Nespaneferhor into the Amun priesthood in regnal year 17 of [[Siamun]], Psusennes II's predecessor—demonstrates that this date was equivalent to 970 BC.<ref>Hornung, Krauss & Warburton, pp.474-475</ref> Since Siamun enjoyed a reign of 19 years, he would have died 2 years later in 968/967 BC and been succeeded by Psusennes II by 967 BC at the latest. Consequently, a reign of 24 years or 967-943 BC is now likely for Psusennes II; hence, his reign has been raised from 14 to 24 years.
+
+ Psusennes II's royal name has been found associated with his successor, [[Shoshenq I]] in a [[Graffito (archaeology)|graffito]] from tomb [[TT18]], and in an [[ostracon]] from [[Umm el-Qa'ab]].<ref>Aidan Dodson, "Psusennes II and Shoshenq I," [[Journal of Egyptian Archaeology|JEA]] 79(1993), pp.267-268</ref>
+
+ ==References==
+ {{reflist}}
+
+-* Aidan Dodson, RdE 38(1987), pp.50-51.
++* Aidan Dodson, RdE 38(1987), pp. 50-51.
+ * Jean Yoyotte, "A propos de Psousennes II," BSSFT 1(1988).
+
++{{DEFAULTSORT:Psusennes Ii}}
+ [[Category:Pharaohs of the Twenty-first dynasty of Egypt]]
+
+ [[cs:Pasbachaenniut II.]]
diff --git a/lib/text/diff/testdata/Top_Gear_Series_14.reverse.uni b/lib/text/diff/testdata/Top_Gear_Series_14.reverse.uni new file mode 100644 index 00000000..5722c1f6 --- /dev/null +++ b/lib/text/diff/testdata/Top_Gear_Series_14.reverse.uni @@ -0,0 +1,11 @@ +--- old ++++ new +@@ -45,7 +45,7 @@ + | 113 || Series 14 Episode 2 || [[22 November]] [[2009]] || [[Michael Sheen]]
+ |- |
+ | colspan="4" valign="top" |
+-'''Review''': Clarkson reviews the new V10-engined [[Audi R8#V10 engine|Audi R8]] which produces 518bhp and a top speed of 199mph. He praises the car immensely, calling it "phenomenal" and that the handling is "epic", saying that it is "spectacularly good" whilst being fairly practical also. However, he thinks that the cup holder is wrongly placed, being in a position that one would knock the contents over whilst changing gear, that the trip computer on the car he tested was broken (although it was later revealed that he had not reset it properly) and that the list price of around £100,000 was too expensive as were the optional extras e.g. ceramic brakes are an extra £7,000 and colour coordinated seat belts £750 plus Audi charge an additional £500 if one were to pick it up from the dealership instead of having it delivered. Overall, he said he would not buy the R8 due to it being "too perfect" and "joyless" stating that it was built purely for speed instead of fun.
++'''Review''': Clarkson reviews the new V10-engined [[Audi R8#V10 engine|Audi R8]] which produces 518bhp and a top speed of 199mph. He praises the car immensely, calling it "phenomenal" and that the handling is "epic", saying that it is "spectacularly good" whilst alse being fairly practical also. However he thinks that the cup holder is wrongly placed, being in a position that one would knock the contents over whilst changing gear, that the trip computer on the car he tested was broken (although it was later revealed that he had not reset it properly) and that the list price of around £100,000 was too expensive as were the optional extras e.g. ceramic brakes are an extra £7,000 and colour coordinated seat belts £750 plus Audi charge an additional £500 if one were to pick it up from the dealership instead of having it delivered. Overall, he said he would not buy the R8 due to it being "too perfect" and "joyless" stating that it was built purely for speed instead of fun.
+
+ Next, he reviews the [[Chevrolet Corvette C6 ZR1|Chevrolet Corvette ZR1]], revealing he would rather buy this than the Audi even though he considers it to be worse than the R8. He praises the car for being great fun and "reallt fast!" It beats the Audi in a drag race as it has 120bhp more than it and weighs considerably less. He reminisces back to the Factual American Road Trip two series ago and reveals that the car he drove then began to fall apart after only three days and refused to start on the fourth. He calls the ZR1 vulgar, hates the fact that it is only available in left hand drive, has a luggage cover which resembles a "motel shower curtain", is much too wide for Britain and that it handles corners in a less than finesse way. He admits to loving the car in California but acknowledges that it might have been a "holiday romance" as it proves to be not as good on the test track and cannot keep up with the R8. Despite all of this, he states that the Corvette is much more fun, although that the Audi is better built, better to drive, better to look at, more comfortable, easier to park and in the "real world" is faster. He believes that you would have to be mad to buy the ZR1 but that is the reason why should.
+
diff --git a/lib/text/diff/testdata/Top_Gear_Series_14.uni b/lib/text/diff/testdata/Top_Gear_Series_14.uni new file mode 100644 index 00000000..32054f7c --- /dev/null +++ b/lib/text/diff/testdata/Top_Gear_Series_14.uni @@ -0,0 +1,11 @@ +--- old ++++ new +@@ -45,7 +45,7 @@ + | 113 || Series 14 Episode 2 || [[22 November]] [[2009]] || [[Michael Sheen]]
+ |- |
+ | colspan="4" valign="top" |
+-'''Review''': Clarkson reviews the new V10-engined [[Audi R8#V10 engine|Audi R8]] which produces 518bhp and a top speed of 199mph. He praises the car immensely, calling it "phenomenal" and that the handling is "epic", saying that it is "spectacularly good" whilst alse being fairly practical also. However he thinks that the cup holder is wrongly placed, being in a position that one would knock the contents over whilst changing gear, that the trip computer on the car he tested was broken (although it was later revealed that he had not reset it properly) and that the list price of around £100,000 was too expensive as were the optional extras e.g. ceramic brakes are an extra £7,000 and colour coordinated seat belts £750 plus Audi charge an additional £500 if one were to pick it up from the dealership instead of having it delivered. Overall, he said he would not buy the R8 due to it being "too perfect" and "joyless" stating that it was built purely for speed instead of fun.
++'''Review''': Clarkson reviews the new V10-engined [[Audi R8#V10 engine|Audi R8]] which produces 518bhp and a top speed of 199mph. He praises the car immensely, calling it "phenomenal" and that the handling is "epic", saying that it is "spectacularly good" whilst being fairly practical also. However, he thinks that the cup holder is wrongly placed, being in a position that one would knock the contents over whilst changing gear, that the trip computer on the car he tested was broken (although it was later revealed that he had not reset it properly) and that the list price of around £100,000 was too expensive as were the optional extras e.g. ceramic brakes are an extra £7,000 and colour coordinated seat belts £750 plus Audi charge an additional £500 if one were to pick it up from the dealership instead of having it delivered. Overall, he said he would not buy the R8 due to it being "too perfect" and "joyless" stating that it was built purely for speed instead of fun.
+
+ Next, he reviews the [[Chevrolet Corvette C6 ZR1|Chevrolet Corvette ZR1]], revealing he would rather buy this than the Audi even though he considers it to be worse than the R8. He praises the car for being great fun and "reallt fast!" It beats the Audi in a drag race as it has 120bhp more than it and weighs considerably less. He reminisces back to the Factual American Road Trip two series ago and reveals that the car he drove then began to fall apart after only three days and refused to start on the fourth. He calls the ZR1 vulgar, hates the fact that it is only available in left hand drive, has a luggage cover which resembles a "motel shower curtain", is much too wide for Britain and that it handles corners in a less than finesse way. He admits to loving the car in California but acknowledges that it might have been a "holiday romance" as it proves to be not as good on the test track and cannot keep up with the R8. Despite all of this, he states that the Corvette is much more fun, although that the Audi is better built, better to drive, better to look at, more comfortable, easier to park and in the "real world" is faster. He believes that you would have to be mad to buy the ZR1 but that is the reason why should.
+
diff --git a/lib/text/diff/testdata/empty_lines.reverse.uni b/lib/text/diff/testdata/empty_lines.reverse.uni new file mode 100644 index 00000000..ebcc2aa9 --- /dev/null +++ b/lib/text/diff/testdata/empty_lines.reverse.uni @@ -0,0 +1,8 @@ +--- old ++++ new +@@ -2,5 +2,3 @@ + + + +- +- diff --git a/lib/text/diff/testdata/empty_lines.uni b/lib/text/diff/testdata/empty_lines.uni new file mode 100644 index 00000000..6503894c --- /dev/null +++ b/lib/text/diff/testdata/empty_lines.uni @@ -0,0 +1,8 @@ +--- old ++++ new +@@ -2,3 +2,5 @@ + + + ++ ++ diff --git a/lib/text/diff/testdata/peeps.reverse.uni b/lib/text/diff/testdata/peeps.reverse.uni new file mode 100644 index 00000000..63b739b3 --- /dev/null +++ b/lib/text/diff/testdata/peeps.reverse.uni @@ -0,0 +1,14 @@ +--- old ++++ new +@@ -20,11 +20,6 @@ + ==Peeps in cooking==
+ While many people eat marshmallow Peeps straight out of the package, they can also be used in a variety of recipes. Peeps can be used as ingredients in such desserts as marshmallow crispy treats, [[fondue]], and [[S'mores|s’mores]]. Peeps are also put into mugs of hot [[cocoa]]; the chicks will float upright until the increasing warmth causes them to dissolve. Although they are made of marshmallow, it is difficult to toast Peeps over a campfire, as the sugar coating tends to burn and become unpalatable.{{Fact|date=May 2009}}
+
+-
+-
+-== Definitionz!!!?? ==
+-A peep is a person involved in a gang or posse, who which blows.
+-
+ ==Contests and competitions==
+
+ An annual "Peep Off" is held in Maryland on the first Saturday after Easter, when Peeps are greatly discounted. The first such event was arranged by Shawn Sparks in 1994, and had only six participants.<ref>{{cite web
diff --git a/lib/text/diff/testdata/peeps.uni b/lib/text/diff/testdata/peeps.uni new file mode 100644 index 00000000..f03547ac --- /dev/null +++ b/lib/text/diff/testdata/peeps.uni @@ -0,0 +1,14 @@ +--- old ++++ new +@@ -20,6 +20,11 @@ + ==Peeps in cooking==
+ While many people eat marshmallow Peeps straight out of the package, they can also be used in a variety of recipes. Peeps can be used as ingredients in such desserts as marshmallow crispy treats, [[fondue]], and [[S'mores|s’mores]]. Peeps are also put into mugs of hot [[cocoa]]; the chicks will float upright until the increasing warmth causes them to dissolve. Although they are made of marshmallow, it is difficult to toast Peeps over a campfire, as the sugar coating tends to burn and become unpalatable.{{Fact|date=May 2009}}
+
++
++
++== Definitionz!!!?? ==
++A peep is a person involved in a gang or posse, who which blows.
++
+ ==Contests and competitions==
+
+ An annual "Peep Off" is held in Maryland on the first Saturday after Easter, when Peeps are greatly discounted. The first such event was arranged by Shawn Sparks in 1994, and had only six participants.<ref>{{cite web
diff --git a/lib/text/diff/testdata/text01.reverse.uni b/lib/text/diff/testdata/text01.reverse.uni new file mode 100644 index 00000000..ae7edb24 --- /dev/null +++ b/lib/text/diff/testdata/text01.reverse.uni @@ -0,0 +1,5 @@ +--- old ++++ new +@@ -1 +1 @@ +-Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref name="Payraudeau, BIFAO 108, p.294">Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records—in the following line—the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref name="Payraudeau, BIFAO 108, p.294"/> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign. ++Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref>Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records--in the following line--the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref>Payraudeau, BIFAO 108, p.294</ref> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign. diff --git a/lib/text/diff/testdata/text01.uni b/lib/text/diff/testdata/text01.uni new file mode 100644 index 00000000..7bb0972e --- /dev/null +++ b/lib/text/diff/testdata/text01.uni @@ -0,0 +1,5 @@ +--- old ++++ new +@@ -1 +1 @@ +-Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref>Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records--in the following line--the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref>Payraudeau, BIFAO 108, p.294</ref> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign. ++Recently, the first conclusive date for king Psusennes II was revealed in a newly published priestly annal stone block. This document, which has been designated as 'Block Karnak 94, CL 2149,' records the induction of a priest named Nesankhefenmaat into the chapel of Amun-Re within the Karnak precint in Year 11 the first month of Shemu day 13 of a king named Psusennes.<ref>Frederic Payraudeau, ''De nouvelles annales sacerdotales de Siamon, Psousennès II et Osorkon Ier.'', BIFAO 108 (2008), p.294</ref> The preceding line of this document recorded the induction of Nesankhefenmaat's father, a certain Nesamun, into the priesthood of Amun-Re in king Siamun's reign.<ref name="Payraudeau, BIFAO 108, p.294">Payraudeau, BIFAO 108, p.294</ref> Siamun was the predecessor of Psusennes II at Tanis. The identification of the aforementioned Psusennes with Psusennes II is certain since the same fragmentary annal document next records—in the following line—the induction of Hor, the son of Nesankhefenmaat, into the priesthood of the chapel of Amun-Re at Karnak in Year 3 the second month of Akhet day 14 of king Osorkon I's reign just one generation later.<ref name="Payraudeau, BIFAO 108, p.294"/> Therefore, the Year 11 date can only be assigned to Psusennes II and constitutes the first securely attested date for this pharaoh's reign. diff --git a/lib/text/diff/testdata/text02.reverse.uni b/lib/text/diff/testdata/text02.reverse.uni new file mode 100644 index 00000000..411811de --- /dev/null +++ b/lib/text/diff/testdata/text02.reverse.uni @@ -0,0 +1,5 @@ +--- old ++++ new +@@ -1 +1 @@ +-In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref name="Kitchen, p.290">Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref name="Kitchen, p.290"/> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14–15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I—a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead—Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years. ++In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref>Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref>Kitchen, p.290</ref> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14-15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I--a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead--Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years. diff --git a/lib/text/diff/testdata/text02.uni b/lib/text/diff/testdata/text02.uni new file mode 100644 index 00000000..d5acbcd4 --- /dev/null +++ b/lib/text/diff/testdata/text02.uni @@ -0,0 +1,5 @@ +--- old ++++ new +@@ -1 +1 @@ +-In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref>Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref>Kitchen, p.290</ref> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14-15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I--a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead--Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years. ++In Year 5 of Shoshenq I, this king and the founder of the 22nd Dynasty dispatched a certain [[Meshwesh|Ma]] (ie. Libyan) subordinate named Wayheset to the desert oasis town of Dakhla in order to restore the king's authority over the western oasis region of Upper Egypt. Wayheset's titles include Prince and Governor of the Oasis. His activities are recorded in the Large Dakhla stela.<ref name="Kitchen, p.290">Kitchen, p.290</ref> This stela states that Wayheset adjudicated in a certain water dispute by consulting a land-register which is explicitly dated to Year 19 of a "Pharaoh Psusennes" in order to determine the water rights of a man named Nysu-Bastet.<ref>[[Alan H. Gardiner]], The Large Dakhla stela, JEA 19 (1930), pp.19-30</ref> Kitchen notes that this individual made an appeal to the Year 19 cadastral land-register of king Psusennes which belonged to his mother which historians assumed was made some "80 years" ago during the reign of Psusennes I.<ref name="Kitchen, p.290"/> The land register recorded that certain water rights were formerly owned by Nysu-Bastet's mother Tewhunet in Year 19 of a king Psusennes. This ruler was generally assumed by Egyptologists to be Psusennes I rather than Psusennes II since the latter's reign was believed to have lasted only 14–15 years. Based on the land register evidence, Wayheset ordered that these watering rights should now be granted to Nysu-Bastet himself. However, if the oracle dated to Year 19 of Psusennes I as many scholars traditionally assumed, Nysu-Bastet would have been separated from his mother by a total of 80 years from this date into Year 5 of Shoshenq I—a figure which is highly unlikely since Nysu-Bastet would not have waited until extreme old age to uphold his mother's watering rights. This implies that the aforementioned king Psusennes here must be identified with Psusennes II instead—Shoshenq I's immediate predecessor and, more significantly, that Psusennes II enjoyed a minimum reign of 19 years. diff --git a/lib/text/diff/testdata/the_singles_collection.reverse.uni b/lib/text/diff/testdata/the_singles_collection.reverse.uni new file mode 100644 index 00000000..47e5526f --- /dev/null +++ b/lib/text/diff/testdata/the_singles_collection.reverse.uni @@ -0,0 +1,113 @@ +--- old ++++ new +@@ -175,64 +175,64 @@ + | contentcss = text-align: left
+ | header = '''Ultimate Fan Box Set/Digital Deluxe Edition'''<ref>http://www.britneyspears.com/2009/10/the-singles-collection-edition-details.php</ref> </br>
+ | content =
+-# "[[...Baby One More Time (song)|...Baby One More Time]]"
++# [[...Baby One More Time (song)|...Baby One More Time]]
+ # "Autumn Goodbye"
+-# "[[Sometimes (Britney Spears song)|Sometimes]]" <small>(Radio Edit)</small>
++# [[Sometimes (Britney Spears song)|Sometimes]] <small>(Radio Edit)</small>
+ # "I'm So Curious"
+-# "[[(You Drive Me) Crazy]]" <small>(The Stop Remix!)</small>
++# [[(You Drive Me) Crazy]] <small>(The Stop Remix!)</small>
+ # "I'll Never Stop Loving You"
+-# "[[Born to Make You Happy]]" <small>(Radio Edit)</small>
+-# "Born to Make You Happy" (Kristian Lundin Bonus Remix)
+-# "[[From the Bottom of My Broken Heart]]" <small>(Radio Edit)</small>
++# [[Born to Make You Happy]] <small>(Radio Edit)</small>
++# "Born to Make You Happy (Kristian Lundin Bonus Remix)"
++# [[From the Bottom of My Broken Heart]] <small>(Radio Edit)</small>
+ # "Thinkin' About You"
+-# "[[Oops!... I Did It Again (song)|Oops!...I Did It Again]]"
++# [[Oops!... I Did It Again (song)|Oops!...I Did It Again]]
+ # "Deep In My Heart"
+-# "[[Lucky (Britney Spears song)|Lucky]]"
++# [[Lucky (Britney Spears song)|Lucky]]
+ # "Heart"
+-# "[[Stronger (Britney Spears song)|Stronger]]"
++# [[Stronger (Britney Spears song)|Stronger]]
+ # "Walk On By"
+-# "[[Don't Let Me Be the Last to Know]]"
+-# "Don't Let Me Be The Last to Know" (Hex Hector Radio Mix)
+-# "[[I'm a Slave 4 U]]"
++# [[Don't Let Me Be the Last to Know]]
++# "Don't Let Me Be The Last to Know (Hex Hector Radio Mix)"
++# [[I'm a Slave 4 U]]
+ # "Intimidated"
+-# "[[Overprotected]]"
++# [[Overprotected]]
+ # "Overprotected" (The Darkchild Remix)
+-# "[[I'm Not a Girl, Not Yet a Woman]]"
++# [[I'm Not a Girl, Not Yet a Woman]]
+ # "I Run Away"
+-# "[[I Love Rock 'n' Roll#Britney Spears version|I Love Rock 'N' Roll]]"
+-# "I'm Not A Girl, Not Yet A Woman" (Metro Remix Radio Edit)
+-# "[[Boys (Britney Spears song)|Boys]]" <small>(The Co-Ed Remix feat. [[Pharrell Williams]]) </small>
+-# "Boys" (Album Version)
+-# "[[Me Against the Music]]" <small>(feat. [[Madonna (entertainer)|Madonna]]) </small>
+-# "Me Against the Music" (Passengerz vs. the Club Mix)
+-# "[[Toxic (song)|Toxic]]"
+-# "Toxic" (Bloodshy & Avant Intoxicated Remix)
+-# "[[Everytime]]"
+-# "Everytime" (Above & Beyond Club Mix)
+-# "[[Outrageous]]"
+-# "Outragous" (Junkie XL's Dancehall Mix)
+-# "[[My Prerogative#Britney Spears version|My Prerogative]]"
+-# "My Prerogative" (Armand Van Helden Remix)
+-# "[[Do Somethin']]"
+-# "Do Somethin'" (Thick Vocal Mix)
+-# "[[Someday (I Will Understand)]]"
++# [[I Love Rock 'n' Roll#Britney Spears version|I Love Rock 'N' Roll]]
++# "I'm Not A Girl, Not Yet A Woman (Metro Remix Radio Edit)"
++# [[Boys (Britney Spears song)|Boys]] <small>(The Co-Ed Remix feat. [[Pharrell Williams]]) </small>
++# "Boys (Album Version)"
++# [[Me Against the Music]] <small>(feat. [[Madonna (entertainer)|Madonna]]) </small>
++# "Me Against the Music (Passengerz vs. the Club Mix)"
++# [[Toxic (song)|Toxic]]
++# "Toxic (Bloodshy & Avant Intoxicated Remix)"
++# [[Everytime]]
++# "Everytime (Above & Beyond Club Mix)"
++# [[Outrageous]]
++# "Outragous (Junkie XL's Dancehall Mix)"
++# [[My Prerogative#Britney Spears version|My Prerogative]]
++# "My Prerogative (Armand Van Helden Remix)"
++# [[Do Somethin']]
++# "Do Somethin' (Thick Vocal Mix)"
++# [[Someday (I Will Understand)]]
+ # "Mona Lisa"
+-# "[[Gimme More]]"
+-# "Gimme More" (Paul Oakenfold Remix)
+-# "[[Piece of Me]]"
+-# "Piece of Me" (Boz O Lo Remix)
+-# "[[Break the Ice (Britney Spears song)|Break the Ice]]"
++# [[Gimme More]]
++# "Gimme More (Paul Oakenfold Remix)"
++# [[Piece of Me]]
++# "Piece of Me (Boz O Lo Remix)"
++# [[Break the Ice (Britney Spears song)|Break the Ice]]
+ # "Everybody"
+-# "[[Womanizer (song)|Womanizer]]"
+-# "Womanizer" (Kaskade Remix)
+-# "[[Circus (song)|Circus]]"
+-# "Circus" (Tom Neville's Ringleader Remix)
+-# "[[If U Seek Amy]]"
+-# "If U Seek Amy" (Crookers Remix)
+-# "[[Radar (song)|Radar]]"
+-# "Radar" (Bloodshy & Avant Remix)
+-# "[[3 (song)|3]]"
+-# "3" (Groove Police Club Mix)
++# [[Womanizer (song)|Womanizer]]
++# "Womanizer (Kaskade Remix)"
++# [[Circus (song)|Circus]]
++# "Circus (Tom Neville's Ringleader Remix)"
++# [[If U Seek Amy]]
++# "If U Seek Amy (Crookers Remix)"
++# [[Radar (song)|Radar]]
++# "Radar (Bloodshy & Avant Remix)"
++# [[3 (song)|3]]
++# "3 (Groove Police Club Mix)"
+ }}
+
+ {{hidden
diff --git a/lib/text/diff/testdata/the_singles_collection.uni b/lib/text/diff/testdata/the_singles_collection.uni new file mode 100644 index 00000000..c2ef7ae5 --- /dev/null +++ b/lib/text/diff/testdata/the_singles_collection.uni @@ -0,0 +1,113 @@ +--- old ++++ new +@@ -175,64 +175,64 @@ + | contentcss = text-align: left
+ | header = '''Ultimate Fan Box Set/Digital Deluxe Edition'''<ref>http://www.britneyspears.com/2009/10/the-singles-collection-edition-details.php</ref> </br>
+ | content =
+-# [[...Baby One More Time (song)|...Baby One More Time]]
++# "[[...Baby One More Time (song)|...Baby One More Time]]"
+ # "Autumn Goodbye"
+-# [[Sometimes (Britney Spears song)|Sometimes]] <small>(Radio Edit)</small>
++# "[[Sometimes (Britney Spears song)|Sometimes]]" <small>(Radio Edit)</small>
+ # "I'm So Curious"
+-# [[(You Drive Me) Crazy]] <small>(The Stop Remix!)</small>
++# "[[(You Drive Me) Crazy]]" <small>(The Stop Remix!)</small>
+ # "I'll Never Stop Loving You"
+-# [[Born to Make You Happy]] <small>(Radio Edit)</small>
+-# "Born to Make You Happy (Kristian Lundin Bonus Remix)"
+-# [[From the Bottom of My Broken Heart]] <small>(Radio Edit)</small>
++# "[[Born to Make You Happy]]" <small>(Radio Edit)</small>
++# "Born to Make You Happy" (Kristian Lundin Bonus Remix)
++# "[[From the Bottom of My Broken Heart]]" <small>(Radio Edit)</small>
+ # "Thinkin' About You"
+-# [[Oops!... I Did It Again (song)|Oops!...I Did It Again]]
++# "[[Oops!... I Did It Again (song)|Oops!...I Did It Again]]"
+ # "Deep In My Heart"
+-# [[Lucky (Britney Spears song)|Lucky]]
++# "[[Lucky (Britney Spears song)|Lucky]]"
+ # "Heart"
+-# [[Stronger (Britney Spears song)|Stronger]]
++# "[[Stronger (Britney Spears song)|Stronger]]"
+ # "Walk On By"
+-# [[Don't Let Me Be the Last to Know]]
+-# "Don't Let Me Be The Last to Know (Hex Hector Radio Mix)"
+-# [[I'm a Slave 4 U]]
++# "[[Don't Let Me Be the Last to Know]]"
++# "Don't Let Me Be The Last to Know" (Hex Hector Radio Mix)
++# "[[I'm a Slave 4 U]]"
+ # "Intimidated"
+-# [[Overprotected]]
++# "[[Overprotected]]"
+ # "Overprotected" (The Darkchild Remix)
+-# [[I'm Not a Girl, Not Yet a Woman]]
++# "[[I'm Not a Girl, Not Yet a Woman]]"
+ # "I Run Away"
+-# [[I Love Rock 'n' Roll#Britney Spears version|I Love Rock 'N' Roll]]
+-# "I'm Not A Girl, Not Yet A Woman (Metro Remix Radio Edit)"
+-# [[Boys (Britney Spears song)|Boys]] <small>(The Co-Ed Remix feat. [[Pharrell Williams]]) </small>
+-# "Boys (Album Version)"
+-# [[Me Against the Music]] <small>(feat. [[Madonna (entertainer)|Madonna]]) </small>
+-# "Me Against the Music (Passengerz vs. the Club Mix)"
+-# [[Toxic (song)|Toxic]]
+-# "Toxic (Bloodshy & Avant Intoxicated Remix)"
+-# [[Everytime]]
+-# "Everytime (Above & Beyond Club Mix)"
+-# [[Outrageous]]
+-# "Outragous (Junkie XL's Dancehall Mix)"
+-# [[My Prerogative#Britney Spears version|My Prerogative]]
+-# "My Prerogative (Armand Van Helden Remix)"
+-# [[Do Somethin']]
+-# "Do Somethin' (Thick Vocal Mix)"
+-# [[Someday (I Will Understand)]]
++# "[[I Love Rock 'n' Roll#Britney Spears version|I Love Rock 'N' Roll]]"
++# "I'm Not A Girl, Not Yet A Woman" (Metro Remix Radio Edit)
++# "[[Boys (Britney Spears song)|Boys]]" <small>(The Co-Ed Remix feat. [[Pharrell Williams]]) </small>
++# "Boys" (Album Version)
++# "[[Me Against the Music]]" <small>(feat. [[Madonna (entertainer)|Madonna]]) </small>
++# "Me Against the Music" (Passengerz vs. the Club Mix)
++# "[[Toxic (song)|Toxic]]"
++# "Toxic" (Bloodshy & Avant Intoxicated Remix)
++# "[[Everytime]]"
++# "Everytime" (Above & Beyond Club Mix)
++# "[[Outrageous]]"
++# "Outragous" (Junkie XL's Dancehall Mix)
++# "[[My Prerogative#Britney Spears version|My Prerogative]]"
++# "My Prerogative" (Armand Van Helden Remix)
++# "[[Do Somethin']]"
++# "Do Somethin'" (Thick Vocal Mix)
++# "[[Someday (I Will Understand)]]"
+ # "Mona Lisa"
+-# [[Gimme More]]
+-# "Gimme More (Paul Oakenfold Remix)"
+-# [[Piece of Me]]
+-# "Piece of Me (Boz O Lo Remix)"
+-# [[Break the Ice (Britney Spears song)|Break the Ice]]
++# "[[Gimme More]]"
++# "Gimme More" (Paul Oakenfold Remix)
++# "[[Piece of Me]]"
++# "Piece of Me" (Boz O Lo Remix)
++# "[[Break the Ice (Britney Spears song)|Break the Ice]]"
+ # "Everybody"
+-# [[Womanizer (song)|Womanizer]]
+-# "Womanizer (Kaskade Remix)"
+-# [[Circus (song)|Circus]]
+-# "Circus (Tom Neville's Ringleader Remix)"
+-# [[If U Seek Amy]]
+-# "If U Seek Amy (Crookers Remix)"
+-# [[Radar (song)|Radar]]
+-# "Radar (Bloodshy & Avant Remix)"
+-# [[3 (song)|3]]
+-# "3 (Groove Police Club Mix)"
++# "[[Womanizer (song)|Womanizer]]"
++# "Womanizer" (Kaskade Remix)
++# "[[Circus (song)|Circus]]"
++# "Circus" (Tom Neville's Ringleader Remix)
++# "[[If U Seek Amy]]"
++# "If U Seek Amy" (Crookers Remix)
++# "[[Radar (song)|Radar]]"
++# "Radar" (Bloodshy & Avant Remix)
++# "[[3 (song)|3]]"
++# "3" (Groove Police Club Mix)
+ }}
+
+ {{hidden
diff --git a/lib/text/diff/testdata/unified/add.uni b/lib/text/diff/testdata/unified/add.uni new file mode 100644 index 00000000..70bcf664 --- /dev/null +++ b/lib/text/diff/testdata/unified/add.uni @@ -0,0 +1,6 @@ +--- old ++++ new +@@ -1,2 +1,3 @@ + a ++b + c diff --git a/lib/text/diff/testdata/unified/del.new b/lib/text/diff/testdata/unified/del.new new file mode 100644 index 00000000..0f7bc766 --- /dev/null +++ b/lib/text/diff/testdata/unified/del.new @@ -0,0 +1,2 @@ +a +c diff --git a/lib/text/diff/testdata/unified/del.old b/lib/text/diff/testdata/unified/del.old new file mode 100644 index 00000000..de980441 --- /dev/null +++ b/lib/text/diff/testdata/unified/del.old @@ -0,0 +1,3 @@ +a +b +c diff --git a/lib/text/diff/testdata/unified/del.uni b/lib/text/diff/testdata/unified/del.uni new file mode 100644 index 00000000..28a260b5 --- /dev/null +++ b/lib/text/diff/testdata/unified/del.uni @@ -0,0 +1,6 @@ +--- old ++++ new +@@ -1,3 +1,2 @@ + a +-b + c diff --git a/lib/text/diff/testdata/unified/mixed.new b/lib/text/diff/testdata/unified/mixed.new new file mode 100644 index 00000000..cc650fa7 --- /dev/null +++ b/lib/text/diff/testdata/unified/mixed.new @@ -0,0 +1,8 @@ +a +b +c +D +E +h +i +J diff --git a/lib/text/diff/testdata/unified/mixed.old b/lib/text/diff/testdata/unified/mixed.old new file mode 100644 index 00000000..92dfa216 --- /dev/null +++ b/lib/text/diff/testdata/unified/mixed.old @@ -0,0 +1,10 @@ +a +b +c +d +e +f +g +h +i +j diff --git a/lib/text/diff/testdata/unified/mixed.uni b/lib/text/diff/testdata/unified/mixed.uni new file mode 100644 index 00000000..04670cef --- /dev/null +++ b/lib/text/diff/testdata/unified/mixed.uni @@ -0,0 +1,16 @@ +--- old ++++ new +@@ -1,10 +1,8 @@ + a + b + c +-d +-e +-f +-g ++D ++E + h + i +-j ++J diff --git a/lib/text/diff/testdata/unified/mixed_reverse.uni b/lib/text/diff/testdata/unified/mixed_reverse.uni new file mode 100644 index 00000000..e62c5097 --- /dev/null +++ b/lib/text/diff/testdata/unified/mixed_reverse.uni @@ -0,0 +1,16 @@ +--- old ++++ new +@@ -1,8 +1,10 @@ + a + b + c +-D +-E ++d ++e ++f ++g + h + i +-J ++j diff --git a/lib/text/diff/testdata/unified/multi_add.uni b/lib/text/diff/testdata/unified/multi_add.uni new file mode 100644 index 00000000..1ba0c42b --- /dev/null +++ b/lib/text/diff/testdata/unified/multi_add.uni @@ -0,0 +1,11 @@ +--- old ++++ new +@@ -3,6 +3,8 @@ + c + d + e ++f + g + h + i ++j diff --git a/lib/text/diff/testdata/unified/multi_del.new b/lib/text/diff/testdata/unified/multi_del.new new file mode 100644 index 00000000..95f79057 --- /dev/null +++ b/lib/text/diff/testdata/unified/multi_del.new @@ -0,0 +1,8 @@ +a +b +c +d +e +g +h +i diff --git a/lib/text/diff/testdata/unified/multi_del.old b/lib/text/diff/testdata/unified/multi_del.old new file mode 100644 index 00000000..92dfa216 --- /dev/null +++ b/lib/text/diff/testdata/unified/multi_del.old @@ -0,0 +1,10 @@ +a +b +c +d +e +f +g +h +i +j diff --git a/lib/text/diff/testdata/unified/multi_del.uni b/lib/text/diff/testdata/unified/multi_del.uni new file mode 100644 index 00000000..8d53fb02 --- /dev/null +++ b/lib/text/diff/testdata/unified/multi_del.uni @@ -0,0 +1,11 @@ +--- old ++++ new +@@ -3,8 +3,6 @@ + c + d + e +-f + g + h + i +-j diff --git a/lib/text/diff/unified.go b/lib/text/diff/unified.go new file mode 100644 index 00000000..6cace40f --- /dev/null +++ b/lib/text/diff/unified.go @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 Shulhan <ms@kilabit.info> + +package diff + +import ( + "fmt" + "io" + "slices" +) + +const defautlNumContext = 3 + +// Unified returns the unified changes between old and new bytes. +// +// References, +// - https://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html +// - https://www.gnu.org/software/diffutils/manual/html_node/Hunks.html +func Unified(old, new []byte) (diff Data) { + diff = Text(old, new, LevelLines) + + // Mergeds the deletions and additions lines. + // The deleted lines must be before the added lines, so we merge them + // first. + + diff.Unified = make([]Line, 0, len(diff.Adds)+len(diff.Dels)+(defautlNumContext*2)) + diff.Unified = append(diff.Unified, diff.Dels...) + + var previdx int + var prev *Line + adds := diff.Adds + if len(adds) != 0 { + prev = &adds[0] + previdx = findInsertIndex(diff.Unified, prev, 0) + if previdx == -1 { + diff.Unified = append(diff.Unified, *prev) + previdx = len(diff.Unified) + } else { + diff.Unified = slices.Insert(diff.Unified, previdx, *prev) + previdx++ + } + adds = adds[1:] + } + for _, line := range adds { + if prev.Num+1 == line.Num { + diff.Unified = slices.Insert(diff.Unified, previdx, line) + prev = &line + previdx++ + continue + } + previdx = findInsertIndex(diff.Unified, &line, previdx) + if previdx == -1 { + diff.Unified = append(diff.Unified, line) + prev = &line + previdx = len(diff.Unified) + continue + } + diff.Unified = slices.Insert(diff.Unified, previdx, line) + prev = &line + previdx++ + } + + return diff +} + +// findInsertIndex returns the slice index to insert the next line into +// unified, beginning from index start. +// +// It will returns -1 if unified is empty or all lines number in unified less +// or equal than next line. +func findInsertIndex(unified []Line, next *Line, start int) (idx int) { + if len(unified) == 0 { + return -1 + } + nextNum := max(next.Num, next.NumOther) + var prev *Line + for start < len(unified) { + line := &unified[start] + if prev == nil { + if line.Num <= nextNum { + prev = line + start++ + continue + } + return start + } + if line.Num <= nextNum { + prev = line + start++ + continue + } + if prev.Num+1 == line.Num { + prev = line + start++ + continue + } + return start + } + return -1 +} + +// WriteUnified writes the result of unified diff into w with ncontext number +// of unmodified lines before and after it. +// +// The ncontext must be 0 or positive, otherwise it will be set to 0 (no +// context added). +func (diff Data) WriteUnified(w io.Writer, ncontext int) (err error) { + if ncontext < 0 { + ncontext = 0 + } + var hunks [][]Line + var hunk []Line + uni := diff.Unified + for len(uni) > 0 { + hunk, uni = diff.hunk(uni, ncontext) + if len(hunk) == 0 { + break + } + hunk = completeHunk(hunk) + hunks = append(hunks, hunk) + } + return diff.writeHunks(w, hunks, false) +} + +// hunk returns combination of unified lines with context lines. +// +// Remember that we use zero index slices with line number start with 1. +// So, assume that current line is at idx 5 (line number 6), +// +// - for the appendBefore, the end must be line.Num-1 (at idx 5). +// +// - for the appendAfter, the start must be line.Num (at idx 6). +// +// idx lines +// 1 +// 2 +// 3 +// 4 {Num:5 Kind:' ' Val:...} +// 5 {Num:6 Kind:'-' Val:...} <-- end (appendBefore) (2,3,4) +// 6 {Num:7 Kind:' ' Val:...} <-- start (appendAfter) (6,7,8) +func (diff *Data) hunk(unified []Line, nctx int) (hunk []Line, remains []Line) { + x := 0 + prev := unified[x] + switch prev.Kind { + case LineKindAdd: + hunk = appendBefore(hunk, diff.newlines, prev.Num-1, nctx) + case LineKindDel: + hunk = appendBefore(hunk, diff.oldlines, prev.Num-1, nctx) + } + hunk = append(hunk, prev) + x++ + if x == len(unified) { + switch prev.Kind { + case LineKindAdd: + hunk = appendAfter(hunk, diff.newlines, prev.Num, nctx) + case LineKindDel: + hunk = appendAfter(hunk, diff.oldlines, prev.Num, nctx) + } + return hunk, nil + } + for x < len(unified) { + line := unified[x] + if prev.Kind == line.Kind { + if prev.Num+1 == line.Num { + // Case, + // 1: -old + // 2: -old + hunk = append(hunk, line) + prev = line + x++ + continue + } + } + count := nctx + gap := computeGap(hunk, prev, line) + if gap <= (nctx * 2) { + count = gap + } + // Add n number of lines after. + switch prev.Kind { + case LineKindAdd: + hunk = appendAfter(hunk, diff.newlines, prev.Num, count) + case LineKindDel: + hunk = appendAfter(hunk, diff.oldlines, prev.Num, count) + } + if gap <= (nctx * 2) { + hunk = append(hunk, line) + prev = line + x++ + continue + } + remains = unified[x:] + return hunk, remains + } + // Changes on the end. + switch prev.Kind { + case LineKindAdd: + hunk = appendAfter(hunk, diff.newlines, prev.Num, nctx) + case LineKindDel: + hunk = appendAfter(hunk, diff.oldlines, prev.Num, nctx) + } + remains = unified[x:] + return hunk, remains +} + +func computeGap(hunk []Line, prev, next Line) (gap int) { + if prev.Kind == next.Kind { + return (next.Num - prev.Num) - 1 + } + // Find the previous line with the same kind as next or unmodified. + for _, line := range slices.Backward(hunk) { + if line.Kind == next.Kind || line.Kind == LineKindNone { + return (next.Num - line.Num) - 1 + } + } + return next.Num - 1 +} + +// appendBefore prepends lines from src at least nctx lines. +func appendBefore(hunk, src []Line, end, nctx int) []Line { + start := max(0, end-nctx) + for start < end { + hunk = append(hunk, src[start]) + start++ + } + return hunk +} + +func appendAfter(hunk, src []Line, start, count int) []Line { + end := min(len(src), start+count) + for start < end { + hunk = append(hunk, src[start]) + start++ + } + return hunk +} + +// completeHunk generates label for hunk "@@ ... @@". +func completeHunk(hunk []Line) []Line { + firstLine := hunk[0] + var firstChange *Line + for _, line := range hunk { + if line.Kind == LineKindNone { + continue + } + firstChange = &line + break + } + + var oldStart, newStart int + + if firstChange.Kind == LineKindAdd { + oldStart = firstLine.NumOther + newStart = firstLine.Num + } else { + oldStart = firstLine.Num + newStart = firstLine.NumOther + } + var oldCount int + var newCount int + for _, line := range hunk { + switch line.Kind { + case LineKindAdd: + newCount++ + case LineKindDel: + oldCount++ + case LineKindNone: + oldCount++ + newCount++ + } + } + + var args = make([]any, 0, 4) + var oldfmt = `-%d,%d` + if oldCount <= 1 { + oldfmt = `-%d` + args = append(args, oldStart) + } else { + args = append(args, oldStart, oldCount) + } + var newfmt = `+%d,%d` + if newCount <= 1 { + newfmt = `+%d` + args = append(args, newStart) + } else { + args = append(args, newStart, newCount) + } + + label := fmt.Sprintf(`@@ `+oldfmt+` `+newfmt+` @@`, args...) + hunkLabel := Line{ + Val: []byte(label), + Kind: LineKindLabel, + } + return slices.Insert(hunk, 0, hunkLabel) +} + +func (diff Data) writeHunks(w io.Writer, hunks [][]Line, withLineNumber bool) (err error) { + fmt.Fprintln(w, `--- old`) + fmt.Fprintln(w, `+++ new`) + for _, hunk := range hunks { + for _, line := range hunk { + switch line.Kind { + case LineKindAdd, LineKindDel: + if withLineNumber { + fmt.Fprintf(w, "%4d:%c%s\n", line.Num, line.Kind, line.Val) + } else { + fmt.Fprintf(w, "%c%s\n", line.Kind, line.Val) + } + case LineKindLabel: + w.Write(line.Val) + w.Write([]byte("\n")) + default: + if withLineNumber { + fmt.Fprintf(w, "%4d: %s\n", line.Num, line.Val) + } else { + fmt.Fprintf(w, " %s\n", line.Val) + } + } + } + } + return nil +} diff --git a/lib/text/diff/unified_test.go b/lib/text/diff/unified_test.go new file mode 100644 index 00000000..bbde5a6a --- /dev/null +++ b/lib/text/diff/unified_test.go @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 Shulhan <ms@kilabit.info> + +package diff + +import ( + "os" + "strings" + "testing" +) + +func TestData_Unified(t *testing.T) { + listCase := []struct { + desc string + oldFile string + newFile string + expFile string + }{{ + desc: `SingleDeletion`, + oldFile: `testdata/unified/del.old`, + newFile: `testdata/unified/del.new`, + expFile: `testdata/unified/del.uni`, + }, { + desc: `MultiDeletion`, + oldFile: `testdata/unified/multi_del.old`, + newFile: `testdata/unified/multi_del.new`, + expFile: `testdata/unified/multi_del.uni`, + }, { + desc: `SingleAddition`, + oldFile: `testdata/unified/del.new`, + newFile: `testdata/unified/del.old`, + expFile: `testdata/unified/add.uni`, + }, { + desc: `MultiAddition`, + oldFile: `testdata/unified/multi_del.new`, + newFile: `testdata/unified/multi_del.old`, + expFile: `testdata/unified/multi_add.uni`, + }, { + desc: `Mixed`, + oldFile: `testdata/unified/mixed.old`, + newFile: `testdata/unified/mixed.new`, + expFile: `testdata/unified/mixed.uni`, + }, { + desc: `MixedReverse`, + oldFile: `testdata/unified/mixed.new`, + newFile: `testdata/unified/mixed.old`, + expFile: `testdata/unified/mixed_reverse.uni`, + }, { + desc: `EmptyLines`, + oldFile: `testdata/empty3lines.txt`, + newFile: `testdata/empty5lines.txt`, + expFile: `testdata/empty_lines.uni`, + }, { + desc: `EmptyLines.reverse`, + oldFile: `testdata/empty5lines.txt`, + newFile: `testdata/empty3lines.txt`, + expFile: `testdata/empty_lines.reverse.uni`, + }, { + desc: `List_of_United_Nations`, + oldFile: `testdata/List_of_United_Nations.old`, + newFile: `testdata/List_of_United_Nations.new`, + expFile: `testdata/List_of_United_Nations.uni`, + }, { + desc: `List_of_United_Nations.reverse`, + oldFile: `testdata/List_of_United_Nations.new`, + newFile: `testdata/List_of_United_Nations.old`, + expFile: `testdata/List_of_United_Nations.reverse.uni`, + }, { + desc: `peeps`, + oldFile: `testdata/peeps.old`, + newFile: `testdata/peeps.new`, + expFile: `testdata/peeps.uni`, + }, { + desc: `peeps.reverse`, + oldFile: `testdata/peeps.new`, + newFile: `testdata/peeps.old`, + expFile: `testdata/peeps.reverse.uni`, + }, { + desc: `Psusennes_II`, + oldFile: `testdata/Psusennes_II.old`, + newFile: `testdata/Psusennes_II.new`, + expFile: `testdata/Psusennes_II.uni`, + }, { + desc: `Psusennes_II.reverse`, + oldFile: `testdata/Psusennes_II.new`, + newFile: `testdata/Psusennes_II.old`, + expFile: `testdata/Psusennes_II.reverse.uni`, + }, { + desc: `text01`, + oldFile: `testdata/text01.old`, + newFile: `testdata/text01.new`, + expFile: `testdata/text01.uni`, + }, { + desc: `text01.reverse`, + oldFile: `testdata/text01.new`, + newFile: `testdata/text01.old`, + expFile: `testdata/text01.reverse.uni`, + }, { + desc: `text02`, + oldFile: `testdata/text02.old`, + newFile: `testdata/text02.new`, + expFile: `testdata/text02.uni`, + }, { + desc: `text02.reverse`, + oldFile: `testdata/text02.new`, + newFile: `testdata/text02.old`, + expFile: `testdata/text02.reverse.uni`, + }, { + desc: `the_singles_collection`, + oldFile: `testdata/the_singles_collection.old`, + newFile: `testdata/the_singles_collection.new`, + expFile: `testdata/the_singles_collection.uni`, + }, { + desc: `the_singles_collection.reverse`, + oldFile: `testdata/the_singles_collection.new`, + newFile: `testdata/the_singles_collection.old`, + expFile: `testdata/the_singles_collection.reverse.uni`, + }, { + desc: `Top_Gear_Series_14`, + oldFile: `testdata/Top_Gear_Series_14.old`, + newFile: `testdata/Top_Gear_Series_14.new`, + expFile: `testdata/Top_Gear_Series_14.uni`, + }, { + desc: `Top_Gear_Series_14reverse.`, + oldFile: `testdata/Top_Gear_Series_14.new`, + newFile: `testdata/Top_Gear_Series_14.old`, + expFile: `testdata/Top_Gear_Series_14.reverse.uni`, + }} + var sbuf strings.Builder + for _, tc := range listCase { + t.Run(tc.desc, func(t *testing.T) { + oldbytes, err := os.ReadFile(tc.oldFile) + if err != nil { + t.Fatal(err) + } + newbytes, err := os.ReadFile(tc.newFile) + if err != nil { + t.Fatal(err) + } + expbytes, err := os.ReadFile(tc.expFile) + if err != nil { + t.Fatal(err) + } + + diff := Unified(oldbytes, newbytes) + sbuf.Reset() + diff.WriteUnified(&sbuf, 3) + exp := string(expbytes) + got := sbuf.String() + if exp != got { + t.Errorf("%s: not matched", tc.desc) + t.Errorf("%s: changes:\n%s", tc.desc, diff.String()) + t.Errorf("%s: want:\n%s", tc.desc, exp) + t.Errorf("%s: got:\n%s", tc.desc, got) + t.Fatal() + } + }) + } +} |
