Update googleurl to the latest version from Chromium

The version used is 85adfe7b2524c588ad7f3804bd84065db8f492bf,
from Tue Nov 3 18:24:30 2020 +0000
diff --git a/.bazelversion b/.bazelversion
index 47b322c..40c341b 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-3.4.1
+3.6.0
diff --git a/AUTHORS b/AUTHORS
index 48cfe49..160949c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -43,6 +43,7 @@
 Aleksandar Stojiljkovic <aleksandar.stojiljkovic@intel.com>
 Alex Gabriel <minilogo@gmail.com>
 Alex Gartrell <agartrell@cmu.edu>
+Alex Gaynor <alex.gaynor@gmail.com>
 Alex Henrie <alexhenrie24@gmail.com>
 Alex Scheele <alexscheele@gmail.com>
 Alexander Douglas <agdoug@amazon.com>
@@ -50,6 +51,7 @@
 Alexander Rezepkin <etu@vivaldi.net>
 Alexander Shalamov <alexander.shalamov@intel.com>
 Alexander Sulfrian <alexander@sulfrian.net>
+Alexander Zhirov <ciberst@gmail.com>
 Alexandre Abreu <wiss1976@gmail.com>
 Alexandru Chiculita <achicu@adobe.com>
 Alexey Korepanov <alexkorep@gmail.com>
@@ -104,6 +106,7 @@
 Arjun Karthik <arjunkar@amazon.com>
 Arman Ghotb <armanghotb@gmail.com>
 Armin Burgmeier <aburgmeier@bloomberg.net>
+Arnaud Coomans <hello@acoomans.com>
 Arnaud Mandy <arnaud.mandy@intel.com>
 Arnaud Renevier <a.renevier@samsung.com>
 Arpita Bahuguna <a.bah@samsung.com>
@@ -166,6 +169,7 @@
 Caitlin Potter <caitpotter88@gmail.com>
 Calvin Mei <calvimei@amazon.com>
 Cameron Gutman <aicommander@gmail.com>
+Carlos Santa <carlos.santa@intel.com>
 Catalin Badea <badea@adobe.com>
 Cathie Chen <cathiechen@tencent.com>
 Cem Kocagil <cem.kocagil@gmail.com>
@@ -183,6 +187,8 @@
 Changyeon Kim <cyzero.kim@samsung.com>
 Chanho Park <parkch98@gmail.com>
 Chansik Yun <chansik.yun@gmail.com>
+Chany Arpin-Plante <chany.arpin@gmail.com>
+Chanyong Moon <dev.chanyongmoon@gmail.com>
 Chaobin Zhang <zhchbin@gmail.com>
 Charles Vaughn <cvaughn@gmail.com>
 Cheng Zhao <zcbenz@gmail.com>
@@ -225,8 +231,9 @@
 Daniel Waxweiler <daniel.waxweiler@gmail.com>
 Dániel Bátyai <dbatyai@inf.u-szeged.hu>
 Dániel Vince <vinced@inf.u-szeged.hu>
+Daoming Qiu <daoming.qiu@intel.com>
 Darshini KN <kn.darshini@samsung.com>
-Dave Barker <kzar@kzar.co.uk>
+Dave Vandyke <kzar@kzar.co.uk>
 David Benjamin <davidben@mit.edu>
 David Davidovic <david@davidovic.io>
 David Erceg <erceg.david@gmail.com>
@@ -238,6 +245,7 @@
 David Manouchehri <david@davidmanouchehri.com>
 David McAllister <mcdavid@amazon.com>
 David Michael Barr <david.barr@samsung.com>
+David Sanders <dsanders11@ucsbalum.com>
 David Spellman <dspell@amazon.com>
 David Valachovic <adenflorian@gmail.com>
 Dax Kelson <dkelson@gurulabs.com>
@@ -255,6 +263,8 @@
 Diego Ferreiro Val <elfogris@gmail.com>
 Dillon Sellars <dill.sellars@gmail.com>
 Divya Bansal <divya.bansal@samsung.com>
+Dmitry Shachnev <mitya57@gmail.com>
+Dmitry Sokolov <dimanne@gmail.com>
 Dominic Farolino <domfarolino@gmail.com>
 Dominic Jodoin <dominic.jodoin@gmail.com>
 Dominik Röttsches <dominik.rottsches@intel.com>
@@ -366,6 +376,7 @@
 Heeyoun Lee <heeyoun.lee@samsung.com>
 Henrique Limas <henrique.ramos.limas@gmail.com>
 Himanshu Joshi <h.joshi@samsung.com>
+Hodol Han <bab6ting@gmail.com>
 Holger Kraus <kraush@amazon.com>
 Hong Zheng <hong.zheng@intel.com>
 Hongbo Min <hongbo.min@intel.com>
@@ -386,7 +397,9 @@
 Hyungchan Kim <inlinechan@gmail.com>
 Hyungwook Lee <hyungwook.lee@navercorp.com>
 Hyungwook Lee <withlhw@gmail.com>
+HyunJi Kim <hjkim3323@gmail.com>
 Hyunjun Shin <hyunjun.shin2@navercorp.com>
+Hyunjun Shin <shjj1504@gmail.com>
 Hyunjune Kim <hyunjune.kim@samsung.com>
 Hyunki Baik <hyunki.baik@samsung.com>
 Ian Cullinan <cullinan@amazon.com>
@@ -408,6 +421,7 @@
 Jacob Clark <jacob.jh.clark@googlemail.com>
 Jacob Mandelson <jacob@mandelson.org>
 Jaehun Lim <ljaehun.lim@samsung.com>
+Jaehyun Ko <jaehyun.dev@gmail.com>
 Jaehyun Lee <j-hyun.lee@samsung.com>
 Jaekyeom Kim <btapiz@gmail.com>
 Jaemin Seo <jaemin86.seo@samsung.com>
@@ -447,6 +461,7 @@
 Jesper Storm Bache <jsbache@gmail.com>
 Jesse Miller <jesse@jmiller.biz>
 Jesus Sanchez-Palencia <jesus.sanchez-palencia.fernandez.fil@intel.com>
+Jiadong Chen <chenjiadong@huawei.com>
 Jiadong Zhu <jiadong.zhu@linaro.org>
 Jiahe Zhang <jiahe.zhang@intel.com>
 Jiajia Qin <jiajia.qin@intel.com>
@@ -510,6 +525,7 @@
 Joyer Huang <collger@gmail.com>
 Juan Jose Lopez Jaimez <jj.lopezjaimez@gmail.com>
 Juhui Lee <juhui24.lee@samsung.com>
+Julian Geppert <spctstr@gmail.com>
 Julien Brianceau <jbriance@cisco.com>
 Julien Isorce <j.isorce@samsung.com>
 Julien Racle <jracle@logitech.com>
@@ -579,6 +595,7 @@
 Kyounga Ra <kyounga.ra@gmail.com>
 Kyoungdeok Kwon <kkd927@gmail.com>
 Kyung Yeol Kim <chitacan@gmail.com>
+Kyungsun Lee <kyungsuny.lee@gmail.com>
 Kyungtae Kim <ktf.kim@samsung.com>
 Kyungyoung Heo <bbvch13531@gmail.com>
 Lalit Chandivade <lalit.chandivade@einfochips.com>
@@ -588,6 +605,7 @@
 Lauren Yeun Kim <lauren.yeun.kim@gmail.com>
 Lauri Oherd <lauri.oherd@gmail.com>
 Lavar Askew <open.hyperion@gmail.com>
+Le Hoang Quyen <le.hoang.q@gmail.com>
 Legend Lee <guanxian.li@intel.com>
 Leith Bade <leith@leithalweapon.geek.nz>
 Lei Li <lli.kernel.kvm@gmail.com>
@@ -605,12 +623,14 @@
 Loo Rong Jie <loorongjie@gmail.com>
 Lorenzo Stoakes <lstoakes@gmail.com>
 Lu Guanqun <guanqun.lu@gmail.com>
+Luc Shi <lei.a.shi@intel.com>
 Luca Di Domenico <luca94dd@gmail.com>
 Lucie Brozkova <lucinka.brozkova@gmail.com>
 Luiz Von Dentz <luiz.von.dentz@intel.com>
 Luka Dojcilovic <l.dojcilovic@gmail.com>
 Lukasz Krakowiak <lukasz.krakowiak@mobica.com>
 Luke Inman-Semerau <luke.semerau@gmail.com>
+Luke Seunghoe Gu <gulukesh@gmail.com>
 Luke Zarko <lukezarko@gmail.com>
 Luoxi Pan <l.panpax@gmail.com>
 Maarten Lankhorst <m.b.lankhorst@gmail.com>
@@ -630,6 +650,7 @@
 Marc des Garets <marc.desgarets@googlemail.com>
 Marcin Wiacek <marcin@mwiacek.com>
 Marco Rodrigues <gothicx@gmail.com>
+Mariam Ali <alimariam@noogler.google.com>
 Mario Pistrich <m.pistrich@gmail.com>
 Mario Sanchez Prada <mario.prada@samsung.com>
 Mariusz Mlynski <marius.mlynski@gmail.com>
@@ -642,6 +663,7 @@
 Martina Kollarova <martina.kollarova@intel.com>
 Masahiro Yado <yado.masa@gmail.com>
 Masaru Nishida <msr.i386@gmail.com>
+Masayuki Wakizaka <mwakizaka0108@gmail.com>
 Matej Knopp <matej.knopp@gmail.com>
 Matheus Bratfisch <matheusbrat@gmail.com>
 Mathias Bynens <mathias@qiwi.be>
@@ -686,8 +708,10 @@
 Milko Leporis <milko.leporis@imgtec.com>
 Milton Chiang <milton.chiang@mediatek.com>
 Milutin Smiljanic <msmiljanic.gm@gmail.com>
+Minchul Kang <tegongkang@gmail.com>
 Minggang Wang <minggang.wang@intel.com>
 Mingmin Xie <melvinxie@gmail.com>
+Minjeong Kim <deoxyribonucleicacid150@gmail.com>
 Minjeong Lee <apenr1234@gmail.com>
 Minseok Koo <kei98301@gmail.com>
 Minsoo Max Koo <msu.koo@samsung.com>
@@ -711,6 +735,7 @@
 Nagarjuna Atluri <nagarjuna.a@samsung.com>
 Naiem Shaik <naiem.shaik@gmail.com>
 Naoki Takano <takano.naoki@gmail.com>
+Nathan Mitchell <nathaniel.v.mitchell@gmail.com>
 Naveen Bobbili <naveenbobbili@motorola.com>
 Naveen Bobbili <qghc36@motorola.com>
 Naveen Kumar Devaraj <devarajn@amazon.com>
@@ -719,6 +744,7 @@
 Neal Gompa <ngompa13@gmail.com>
 Ned Williamson <nedwilliamson@gmail.com>
 Nedeljko Babic <nedeljko.babic@imgtec.com>
+Nidhi Jaju <nidhijaju127@gmail.com>
 Nikhil Bansal <n.bansal@samsung.com>
 Nikhil Sahni <nikhil.sahni@samsung.com>
 Nikita Ofitserov <himikof@gmail.com>
@@ -771,6 +797,7 @@
 Peter Griffin <peter.griffin@linaro.org>
 Peter Molnar <pmolnar.u-szeged@partner.samsung.com>
 Peter Snyder <snyderp@gmail.com>
+Peter Varga <pvarga@inf.u-szeged.hu>
 Peter Wong <peter.wm.wong@gmail.com>
 Philip Hanson <philip.hanson@intel.com>
 Philipp Hancke <fippo@andyet.net>
@@ -792,6 +819,7 @@
 Pritam Nikam <pritam.nikam@samsung.com>
 Puttaraju R <puttaraju.r@samsung.com>
 Qi Yang <qi1988.yang@samsung.com>
+Qiang Zeng <zengqiang1@huawei.com>
 Qiankun Miao <qiankun.miao@intel.com>
 Qing Zhang <qing.zhang@intel.com>
 Qingmei Li <qingmei.li@vivo.com>
@@ -815,6 +843,7 @@
 Ravi Phaneendra Kasibhatla <r.kasibhatla@samsung.com>
 Ravi Phaneendra Kasibhatla <ravi.kasibhatla@motorola.com>
 Raviraj Sitaram <raviraj.p.sitaram@intel.com>
+Reda Tawfik <redatawfik@noogler.google.com>
 Réda Housni Alaoui <alaoui.rda@gmail.com>
 Refael Ackermann <refack@gmail.com>
 Renata Hodovan <rhodovan.u-szeged@partner.samsung.com>
@@ -833,6 +862,7 @@
 Robert Hogan <robhogan@gmail.com>
 Robert Nagy <robert.nagy@gmail.com>
 Robert Sesek <rsesek@bluestatic.org>
+Roger Zanoni <rogerzanoni@gmail.com>
 Roland Takacs <rtakacs.u-szeged@partner.samsung.com>
 Romain Pokrzywka <romain.pokrzywka@gmail.com>
 Rosen Dash <nqk836@motorola.com>
@@ -844,6 +874,7 @@
 Ruben Terrazas <rubentopo@gmail.com>
 Rufus Hamade <rufus.hamade@imgtec.com>
 Ruiyi Luo <luoruiyi2008@gmail.com>
+Rulong Chen <rulong.crl@alibaba-inc.com>
 Russell Davis <russell.davis@gmail.com>
 Ryan Ackley <ryanackley@gmail.com>
 Ryan Norton <rnorton10@gmail.com>
@@ -885,6 +916,7 @@
 Sebastian Krzyszkowiak <dos@dosowisko.net>
 Seo Sanghyeon <sanxiyn@gmail.com>
 Seokju Kwon <seokju.kwon@gmail.com>
+Seokho Song <0xdevssh@gmail.com>
 SeongTae Jeong <ferendevelop.gl@gmail.com>
 Sergey Kipet <sergey.kipet@gmail.com>
 Sergey Putilin <p.sergey@samsung.com>
@@ -908,6 +940,7 @@
 Sherry Mou <wenjinm@amazon.com>
 Shez Baig <sbaig1@bloomberg.net>
 Shigeki Ohtsu <shigeki.ohtsu@gmail.com>
+Shicheng Wang <wangshicheng@xiaomi.com>
 Shiliu Wang <aofdwsl@gmail.com>
 Shiliu Wang <shiliu.wang@intel.com>
 Shilpa Shri <shilpa.shri@samsung.com>
@@ -942,6 +975,7 @@
 Stephen Searles <stephen.searles@gmail.com>
 Stephen Sigwart <ssigwart@gmail.com>
 Steve Sanders <steve@zanderz.com>
+Steven Cohen <peragwin@gmail.com>
 Steven Pennington <spenn@engr.uvic.ca>
 Steven Roussey <sroussey@gmail.com>
 Subrahmanya Praveen Munukutla <sataya.m@samsung.com>
@@ -951,6 +985,7 @@
 Sujae Jo <sujae33.jo@gmail.com>
 Sujith S S <sujiths.s@samsung.com>
 Sunchang Li <johnstonli@tencent.com>
+Sundoo Kim <nerdooit@gmail.com>
 Suneel Kota <suneel.kota@samsung.com>
 Sungguk Lim <limasdf@gmail.com>
 Sungmann Cho <sungmann.cho@gmail.com>
@@ -971,6 +1006,7 @@
 Taeheon Kim <skyrabbits1@gmail.com>
 Taeho Nam <thn7440@gmail.com>
 Taehoon Lee <taylor.hoon@gmail.com>
+Tae Shin <taeshindev@gmail.com>
 Takashi Fujita <tgfjt.mail@gmail.com>
 Takeshi Kurosawa <taken.spc@gmail.com>
 Tanay Chowdhury <tanay.c@samsung.com>
@@ -984,6 +1020,7 @@
 Thiago Marcos P. Santos <thiago.santos@intel.com>
 Thomas Butter <tbutter@gmail.com>
 Thomas Conti <tomc@amazon.com>
+Thomas Nguyen <haitung.nguyen@avast.com>
 Thomas White <im.toms.inbox@gmail.com>
 Tiago Vignatti <tiago.vignatti@intel.com>
 Tibor Dusnoki <tibor.dusnoki.91@gmail.com>
@@ -1037,11 +1074,13 @@
 Wesley Lancel <wesleylancel@gmail.com>
 Wei Wang <wei4.wang@intel.com>
 Wesley Wigham <wwigham@gmail.com>
+Will Cohen <wwcohen@gmail.com>
 Will Hirsch <chromium@willhirsch.co.uk>
 Will Shackleton <w.shackleton@gmail.com>
 William Xie <william.xie@intel.com>
 Winston Chen <winston.c1@samsung.com>
 Xiang Long <xiang.long@intel.com>
+XiangYang <yangxiang12@huawei.com>
 Xiangze Zhang <xiangze.zhang@intel.com>
 Xiaofeng Zhang <xiaofeng.zhang@intel.com>
 Xiaolei Yu <dreifachstein@gmail.com>
@@ -1071,6 +1110,7 @@
 Yi Sun <ratsunny@gmail.com>
 Yichen Jiang <jiangyichen123@gmail.com>
 Yifei Yu <yuyifei@xiaomi.com>
+Yi Zhang <yi.y.zhang@intel.com>
 Yizhou Jiang <yizhou.jiang@intel.com>
 Yoav Weiss <yoav@yoav.ws>
 Yoav Zilberberg <yoav.zilberberg@gmail.com>
@@ -1116,8 +1156,7 @@
 Zoltan Kuscsik <zoltan.kuscsik@linaro.org>
 Zsolt Borbely <zsborbely.u-szeged@partner.samsung.com>
 方觉 (Fang Jue) <fangjue23303@gmail.com>
-Julian Geppert <spctstr@gmail.com>
-Jiadong Chen <chenjiadong@huawei.com>
+# Please DO NOT APPEND here. See comments at the top of the file.
 # END individuals section.
 
 # BEGIN organizations section.
@@ -1130,8 +1169,10 @@
 Cloudflare, Inc. <*@cloudflare.com>
 CloudMosa, Inc. <*@cloudmosa.com>
 Code Aurora Forum <*@codeaurora.org>
+CodeWeavers, Inc. <*@codeweavers.com>
 Collabora Limited <*@collabora.com>
 Comodo CA Limited
+CoSMo Software pvt ltd <*@cosmosoftware.io>
 Cosium <*@cosium.com>
 Dell Technologies Inc. <*@dell.corp-partner.google.com>
 Duck Duck Go, Inc. <*@duckduckgo.com>
@@ -1140,7 +1181,9 @@
 Facebook, Inc. <*@fb.com>
 Facebook, Inc. <*@oculus.com>
 Google Inc. <*@google.com>
+Grammarly, Inc. <*@grammarly.com>
 Hewlett-Packard Development Company, L.P. <*@hp.com>
+HyperConnect Inc. <*@hpcnt.com>
 IBM Inc. <*@*.ibm.com>
 IBM Inc. <*@ibm.com>
 Igalia S.L. <*@igalia.com>
@@ -1160,6 +1203,7 @@
 Neverware Inc. <*@neverware.com>
 NIKE, Inc. <*@nike.com>
 NVIDIA Corporation <*@nvidia.com>
+OpenFin Inc. <*@openfin.co>
 Opera Software ASA <*@opera.com>
 Optical Tone Ltd <*@opticaltone.com>
 Pengutronix e.K. <*@pengutronix.de>
@@ -1179,4 +1223,5 @@
 Vewd Software AS <*@vewd.com>
 Vivaldi Technologies AS <*@vivaldi.com>
 Yandex LLC <*@yandex-team.ru>
+# Please DO NOT APPEND here. See comments at the top of the file.
 # END organizations section.
diff --git a/base/BUILD b/base/BUILD
index 00c6c91..2ab77e8 100644
--- a/base/BUILD
+++ b/base/BUILD
@@ -9,12 +9,19 @@
     hdrs = [
         "compiler_specific.h",
         "containers/checked_iterators.h",
+        "containers/contiguous_iterator.h",
         "containers/span.h",
         "containers/util.h",
         "debug/leak_annotations.h",
+        "functional/identity.h",
+        "functional/invoke.h",
+        "functional/not_fn.h",
         "macros.h",
         "no_destructor.h",
         "optional.h",
+        "ranges/algorithm.h",
+        "ranges/functional.h",
+        "ranges/ranges.h",
         "stl_util.h",
         "template_util.h",
     ],
diff --git a/base/compiler_specific.h b/base/compiler_specific.h
index 0cd36dc..0f960c1 100644
--- a/base/compiler_specific.h
+++ b/base/compiler_specific.h
@@ -158,6 +158,19 @@
 #endif
 #endif
 
+// DISABLE_CFI_ICALL -- Disable Control Flow Integrity indirect call checks.
+#if !defined(DISABLE_CFI_ICALL)
+#if defined(OS_WIN)
+// Windows also needs __declspec(guard(nocf)).
+#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") __declspec(guard(nocf))
+#else
+#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall")
+#endif
+#endif
+#if !defined(DISABLE_CFI_ICALL)
+#define DISABLE_CFI_ICALL
+#endif
+
 // Macro useful for writing cross-platform function pointers.
 #if !defined(CDECL)
 #if defined(OS_WIN)
diff --git a/base/containers/contiguous_iterator.h b/base/containers/contiguous_iterator.h
new file mode 100644
index 0000000..a1c1f9b
--- /dev/null
+++ b/base/containers/contiguous_iterator.h
@@ -0,0 +1,97 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_
+#define BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_
+
+#include <array>
+#include <iterator>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "base/containers/checked_iterators.h"
+#include "base/template_util.h"
+
+namespace gurl_base {
+
+namespace internal {
+
+template <typename T>
+struct PointsToObject : std::is_object<iter_value_t<T>> {};
+
+// A pointer is a contiguous iterator.
+// Reference: https://wg21.link/iterator.traits#5
+template <typename T>
+struct IsPointer : std::is_pointer<T> {};
+
+// An iterator to std::basic_string is contiguous.
+// Reference: https://wg21.link/basic.string.general#2
+template <typename T, typename StringT = std::basic_string<iter_value_t<T>>>
+struct IsStringIter
+    : conjunction<std::is_trivial<iter_value_t<T>>,
+                  disjunction<std::is_same<T, typename StringT::const_iterator>,
+                              std::is_same<T, typename StringT::iterator>>> {};
+
+// An iterator to std::array is contiguous.
+// Reference: https://wg21.link/array.overview#1
+template <typename T, typename ArrayT = std::array<iter_value_t<T>, 1>>
+struct IsArrayIter
+    : disjunction<std::is_same<T, typename ArrayT::const_iterator>,
+                  std::is_same<T, typename ArrayT::iterator>> {};
+
+// An iterator to a non-bool std::vector is contiguous.
+// Reference: https://wg21.link/vector.overview#2
+template <typename T, typename VectorT = std::vector<iter_value_t<T>>>
+struct IsVectorIter
+    : conjunction<negation<std::is_same<iter_value_t<T>, bool>>,
+                  disjunction<std::is_same<T, typename VectorT::const_iterator>,
+                              std::is_same<T, typename VectorT::iterator>>> {};
+
+// The result of passing a std::valarray to std::begin is a contiguous iterator.
+// Note: Since all common standard library implementations (i.e. libc++,
+// stdlibc++ and MSVC's STL) just use a pointer here, we perform a similar
+// optimization. The corresponding unittest still ensures that this is working
+// as intended.
+// Reference: https://wg21.link/valarray.range#1
+template <typename T>
+struct IsValueArrayIter : std::is_pointer<T> {};
+
+// base's CheckedContiguousIterator is a contiguous iterator.
+template <typename T, typename ValueT = iter_value_t<T>>
+struct IsCheckedContiguousIter
+    : disjunction<std::is_same<T, gurl_base::CheckedContiguousConstIterator<ValueT>>,
+                  std::is_same<T, gurl_base::CheckedContiguousIterator<ValueT>>> {};
+
+// Check that the iterator points to an actual object, and is one of the
+// iterator types mentioned above.
+template <typename T>
+struct IsContiguousIteratorImpl
+    : conjunction<PointsToObject<T>,
+                  disjunction<IsPointer<T>,
+                              IsStringIter<T>,
+                              IsArrayIter<T>,
+                              IsVectorIter<T>,
+                              IsValueArrayIter<T>,
+                              IsCheckedContiguousIter<T>>> {};
+
+}  // namespace internal
+
+// IsContiguousIterator is a type trait that determines whether a given type is
+// a contiguous iterator. It is similar to C++20's contiguous_iterator concept,
+// but due to a lack of the corresponding contiguous_iterator_tag relies on
+// explicitly instantiating the type with iterators that are supposed to be
+// contiguous iterators.
+// References:
+// - https://wg21.link/iterator.concept.contiguous
+// - https://wg21.link/std.iterator.tags#lib:contiguous_iterator_tag
+// - https://wg21.link/n4284
+template <typename T>
+struct IsContiguousIterator
+    : internal::IsContiguousIteratorImpl<
+          std::remove_cv_t<std::remove_reference_t<T>>> {};
+
+}  // namespace base
+
+#endif  // BASE_CONTAINERS_CONTIGUOUS_ITERATOR_H_
diff --git a/base/containers/span.h b/base/containers/span.h
index 45a322d..53b6965 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -16,6 +16,7 @@
 
 #include "polyfills/base/check_op.h"
 #include "base/containers/checked_iterators.h"
+#include "base/containers/contiguous_iterator.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/template_util.h"
@@ -72,6 +73,15 @@
 template <typename From, typename To>
 using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
 
+template <typename Iter, typename T>
+using IteratorHasConvertibleReferenceType =
+    IsLegalDataConversion<std::remove_reference_t<iter_reference_t<Iter>>, T>;
+
+template <typename Iter, typename T>
+using EnableIfCompatibleContiguousIterator = std::enable_if_t<
+    conjunction<IsContiguousIterator<Iter>,
+                IteratorHasConvertibleReferenceType<Iter, T>>::value>;
+
 template <typename Container, typename T>
 using ContainerHasConvertibleData = IsLegalDataConversion<
     std::remove_pointer_t<decltype(gurl_base::data(std::declval<Container>()))>,
@@ -132,6 +142,16 @@
   size_t size_;
 };
 
+// must_not_be_dynamic_extent prevents |dynamic_extent| from being returned in a
+// constexpr context.
+template <size_t kExtent>
+constexpr size_t must_not_be_dynamic_extent() {
+  static_assert(
+      kExtent != dynamic_extent,
+      "EXTENT should only be used for containers with a static extent.");
+  return kExtent;
+}
+
 }  // namespace internal
 
 // A span is a value type that represents an array of elements of type T. Since
@@ -240,14 +260,19 @@
     static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
   }
 
-  constexpr span(T* data, size_t size) noexcept
-      : ExtentStorage(size), data_(data) {
-    GURL_CHECK(Extent == dynamic_extent || Extent == size);
+  template <typename It,
+            typename = internal::EnableIfCompatibleContiguousIterator<It, T>>
+  constexpr span(It first, size_t count) noexcept
+      : ExtentStorage(count), data_(gurl_base::to_address(first)) {
+    GURL_CHECK(Extent == dynamic_extent || Extent == count);
   }
 
-  // Artificially templatized to break ambiguity for span(ptr, 0).
-  template <typename = void>
-  constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
+  template <
+      typename It,
+      typename End,
+      typename = internal::EnableIfCompatibleContiguousIterator<It, T>,
+      typename = std::enable_if_t<!std::is_convertible<End, size_t>::value>>
+  constexpr span(It begin, End end) noexcept : span(begin, end - begin) {
     // Note: GURL_CHECK_LE is not constexpr, hence regular GURL_CHECK must be used.
     GURL_CHECK(begin <= end);
   }
@@ -432,14 +457,10 @@
 }
 
 // Type-deducing helpers for constructing a span.
-template <int&... ExplicitArgumentBarrier, typename T>
-constexpr span<T> make_span(T* data, size_t size) noexcept {
-  return {data, size};
-}
-
-template <int&... ExplicitArgumentBarrier, typename T>
-constexpr span<T> make_span(T* begin, T* end) noexcept {
-  return {begin, end};
+template <int&... ExplicitArgumentBarrier, typename It, typename EndOrSize>
+constexpr auto make_span(It it, EndOrSize end_or_size) noexcept {
+  using T = std::remove_reference_t<iter_reference_t<It>>;
+  return span<T>(it, end_or_size);
 }
 
 // make_span utility function that deduces both the span's value_type and extent
@@ -462,6 +483,15 @@
 // Note: This will GURL_CHECK that N indeed matches size(container).
 //
 // Usage: auto static_span = gurl_base::make_span<N>(...);
+template <size_t N,
+          int&... ExplicitArgumentBarrier,
+          typename It,
+          typename EndOrSize>
+constexpr auto make_span(It it, EndOrSize end_or_size) noexcept {
+  using T = std::remove_reference_t<iter_reference_t<It>>;
+  return span<T, N>(it, end_or_size);
+}
+
 template <size_t N, int&... ExplicitArgumentBarrier, typename Container>
 constexpr auto make_span(Container&& container) noexcept {
   using T =
@@ -471,4 +501,15 @@
 
 }  // namespace base
 
+// EXTENT returns the size of any type that can be converted to a |gurl_base::span|
+// with definite extent, i.e. everything that is a contiguous storage of some
+// sort with static size. Specifically, this works for std::array in a constexpr
+// context. Note:
+//   * |gurl_base::size| should be preferred for plain arrays.
+//   * In run-time contexts, functions such as |std::array::size| should be
+//     preferred.
+#define EXTENT(x)                                        \
+  ::gurl_base::internal::must_not_be_dynamic_extent<decltype( \
+      ::gurl_base::make_span(x))::extent>()
+
 #endif  // BASE_CONTAINERS_SPAN_H_
diff --git a/base/functional/identity.h b/base/functional/identity.h
new file mode 100644
index 0000000..26af8e0
--- /dev/null
+++ b/base/functional/identity.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FUNCTIONAL_IDENTITY_H_
+#define BASE_FUNCTIONAL_IDENTITY_H_
+
+#include <utility>
+
+namespace gurl_base {
+
+// Implementation of C++20's std::identity.
+//
+// Reference:
+// - https://en.cppreference.com/w/cpp/utility/functional/identity
+// - https://wg21.link/func.identity
+struct identity {
+  template <typename T>
+  constexpr T&& operator()(T&& t) const noexcept {
+    return std::forward<T>(t);
+  }
+
+  using is_transparent = void;
+};
+
+}  // namespace base
+
+#endif  // BASE_FUNCTIONAL_IDENTITY_H_
diff --git a/base/functional/invoke.h b/base/functional/invoke.h
new file mode 100644
index 0000000..4399c6b
--- /dev/null
+++ b/base/functional/invoke.h
@@ -0,0 +1,152 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FUNCTIONAL_INVOKE_H_
+#define BASE_FUNCTIONAL_INVOKE_H_
+
+#include <type_traits>
+#include <utility>
+
+namespace gurl_base {
+
+namespace internal {
+
+// Helper struct and alias to deduce the class type from a member function
+// pointer or member object pointer.
+template <typename DecayedF>
+struct member_pointer_class {};
+
+template <typename ReturnT, typename ClassT>
+struct member_pointer_class<ReturnT ClassT::*> {
+  using type = ClassT;
+};
+
+template <typename DecayedF>
+using member_pointer_class_t = typename member_pointer_class<DecayedF>::type;
+
+// Utility struct to detect specializations of std::reference_wrapper.
+template <typename T>
+struct is_reference_wrapper : std::false_type {};
+
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+// Small helpers used below in internal::invoke to make the SFINAE more concise.
+template <typename F>
+const bool& IsMemFunPtr =
+    std::is_member_function_pointer<std::decay_t<F>>::value;
+
+template <typename F>
+const bool& IsMemObjPtr = std::is_member_object_pointer<std::decay_t<F>>::value;
+
+template <typename F,
+          typename T,
+          typename MemPtrClass = member_pointer_class_t<std::decay_t<F>>>
+const bool& IsMemPtrToBaseOf =
+    std::is_base_of<MemPtrClass, std::decay_t<T>>::value;
+
+template <typename T>
+const bool& IsRefWrapper = is_reference_wrapper<std::decay_t<T>>::value;
+
+template <bool B>
+using EnableIf = std::enable_if_t<B, bool>;
+
+// Invokes a member function pointer on a reference to an object of a suitable
+// type. Covers bullet 1 of the INVOKE definition.
+//
+// Reference: https://wg21.link/func.require#1.1
+template <typename F,
+          typename T1,
+          typename... Args,
+          EnableIf<IsMemFunPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>
+constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
+  return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);
+}
+
+// Invokes a member function pointer on a std::reference_wrapper to an object of
+// a suitable type. Covers bullet 2 of the INVOKE definition.
+//
+// Reference: https://wg21.link/func.require#1.2
+template <typename F,
+          typename T1,
+          typename... Args,
+          EnableIf<IsMemFunPtr<F> && IsRefWrapper<T1>> = true>
+constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
+  return (t1.get().*f)(std::forward<Args>(args)...);
+}
+
+// Invokes a member function pointer on a pointer-like type to an object of a
+// suitable type. Covers bullet 3 of the INVOKE definition.
+//
+// Reference: https://wg21.link/func.require#1.3
+template <typename F,
+          typename T1,
+          typename... Args,
+          EnableIf<IsMemFunPtr<F> && !IsMemPtrToBaseOf<F, T1> &&
+                   !IsRefWrapper<T1>> = true>
+constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
+  return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
+}
+
+// Invokes a member object pointer on a reference to an object of a suitable
+// type. Covers bullet 4 of the INVOKE definition.
+//
+// Reference: https://wg21.link/func.require#1.4
+template <typename F,
+          typename T1,
+          EnableIf<IsMemObjPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>
+constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
+  return std::forward<T1>(t1).*f;
+}
+
+// Invokes a member object pointer on a std::reference_wrapper to an object of
+// a suitable type. Covers bullet 5 of the INVOKE definition.
+//
+// Reference: https://wg21.link/func.require#1.5
+template <typename F,
+          typename T1,
+          EnableIf<IsMemObjPtr<F> && IsRefWrapper<T1>> = true>
+constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
+  return t1.get().*f;
+}
+
+// Invokes a member object pointer on a pointer-like type to an object of a
+// suitable type. Covers bullet 6 of the INVOKE definition.
+//
+// Reference: https://wg21.link/func.require#1.6
+template <typename F,
+          typename T1,
+          EnableIf<IsMemObjPtr<F> && !IsMemPtrToBaseOf<F, T1> &&
+                   !IsRefWrapper<T1>> = true>
+constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1) {
+  return (*std::forward<T1>(t1)).*f;
+}
+
+// Invokes a regular function or function object. Covers bullet 7 of the INVOKE
+// definition.
+//
+// Reference: https://wg21.link/func.require#1.7
+template <typename F, typename... Args>
+constexpr decltype(auto) InvokeImpl(F&& f, Args&&... args) {
+  return std::forward<F>(f)(std::forward<Args>(args)...);
+}
+
+}  // namespace internal
+
+// Implementation of C++17's std::invoke. This is not based on implementation
+// referenced in original std::invoke proposal, but rather a manual
+// implementation, so that it can be constexpr.
+//
+// References:
+// - https://wg21.link/n4169#implementability
+// - https://en.cppreference.com/w/cpp/utility/functional/invoke
+// - https://wg21.link/func.invoke
+template <typename F, typename... Args>
+constexpr decltype(auto) invoke(F&& f, Args&&... args) {
+  return internal::InvokeImpl(std::forward<F>(f), std::forward<Args>(args)...);
+}
+
+}  // namespace base
+
+#endif  // BASE_FUNCTIONAL_INVOKE_H_
diff --git a/base/functional/not_fn.h b/base/functional/not_fn.h
new file mode 100644
index 0000000..7457d03
--- /dev/null
+++ b/base/functional/not_fn.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FUNCTIONAL_NOT_FN_H_
+#define BASE_FUNCTIONAL_NOT_FN_H_
+
+#include <type_traits>
+#include <utility>
+
+#include "base/functional/invoke.h"
+
+namespace gurl_base {
+
+namespace internal {
+
+template <typename F>
+struct NotFnImpl {
+  F f;
+
+  template <typename... Args>
+  constexpr decltype(auto) operator()(Args&&... args) & noexcept {
+    return !gurl_base::invoke(f, std::forward<Args>(args)...);
+  }
+
+  template <typename... Args>
+  constexpr decltype(auto) operator()(Args&&... args) const& noexcept {
+    return !gurl_base::invoke(f, std::forward<Args>(args)...);
+  }
+
+  template <typename... Args>
+  constexpr decltype(auto) operator()(Args&&... args) && noexcept {
+    return !gurl_base::invoke(std::move(f), std::forward<Args>(args)...);
+  }
+
+  template <typename... Args>
+  constexpr decltype(auto) operator()(Args&&... args) const&& noexcept {
+    return !gurl_base::invoke(std::move(f), std::forward<Args>(args)...);
+  }
+};
+
+}  // namespace internal
+
+// Implementation of C++17's std::not_fn.
+//
+// Reference:
+// - https://en.cppreference.com/w/cpp/utility/functional/not_fn
+// - https://wg21.link/func.not.fn
+template <typename F>
+constexpr internal::NotFnImpl<std::decay_t<F>> not_fn(F&& f) {
+  return {std::forward<F>(f)};
+}
+
+}  // namespace base
+
+#endif  // BASE_FUNCTIONAL_NOT_FN_H_
diff --git a/base/optional.h b/base/optional.h
index f07cc66..c946364 100644
--- a/base/optional.h
+++ b/base/optional.h
@@ -61,7 +61,7 @@
   template <class... Args>
   void Init(Args&&... args) {
     GURL_DCHECK(!is_populated_);
-    ::new (&value_) T(std::forward<Args>(args)...);
+    ::new (std::addressof(value_)) T(std::forward<Args>(args)...);
     is_populated_ = true;
   }
 
@@ -111,7 +111,7 @@
   template <class... Args>
   void Init(Args&&... args) {
     GURL_DCHECK(!is_populated_);
-    ::new (&value_) T(std::forward<Args>(args)...);
+    ::new (std::addressof(value_)) T(std::forward<Args>(args)...);
     is_populated_ = true;
   }
 
@@ -607,12 +607,12 @@
 
   constexpr const T* operator->() const {
     GURL_CHECK(storage_.is_populated_);
-    return &storage_.value_;
+    return std::addressof(storage_.value_);
   }
 
   constexpr T* operator->() {
     GURL_CHECK(storage_.is_populated_);
-    return &storage_.value_;
+    return std::addressof(storage_.value_);
   }
 
   constexpr const T& operator*() const & {
diff --git a/base/ranges/algorithm.h b/base/ranges/algorithm.h
new file mode 100644
index 0000000..649059f
--- /dev/null
+++ b/base/ranges/algorithm.h
@@ -0,0 +1,4996 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_RANGES_ALGORITHM_H_
+#define BASE_RANGES_ALGORITHM_H_
+
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "base/functional/identity.h"
+#include "base/functional/invoke.h"
+#include "base/ranges/functional.h"
+#include "base/ranges/ranges.h"
+#include "base/template_util.h"
+
+namespace gurl_base {
+
+namespace internal {
+
+// Returns a transformed version of the unary predicate `pred` applying `proj`
+// to its argument before invoking `pred` on it.
+// Ensures that the return type of `invoke(pred, ...)` is convertible to bool.
+template <typename Pred, typename Proj>
+constexpr auto ProjectedUnaryPredicate(Pred& pred, Proj& proj) noexcept {
+  return [&pred, &proj](auto&& arg) -> bool {
+    return gurl_base::invoke(pred,
+                        gurl_base::invoke(proj, std::forward<decltype(arg)>(arg)));
+  };
+}
+
+// Returns a transformed version of the binary predicate `pred` applying `proj1`
+// and `proj2` to its arguments before invoking `pred` on them.
+//
+// Provides an opt-in to considers all four permutations of projections and
+// argument types. This is sometimes necessary to allow usage with legacy
+// non-ranges std:: algorithms that don't support projections.
+//
+// These permutations are assigned different priorities to break ambiguities in
+// case several permutations are possible, e.g. when Proj1 and Proj2 are the
+// same type.
+//
+// Note that even when opting in to using all permutations of projections,
+// calling code should still ensure that the canonical mapping of {Proj1, Proj2}
+// to {LHS, RHS} compiles for all members of the range. This can be done by
+// adding the following constraint:
+//
+//   typename = indirect_result_t<Pred&,
+//                                projected<iterator_t<Range1>, Proj1>,
+//                                projected<iterator_t<Range2>, Proj2>>
+//
+// Ensures that the return type of `invoke(pred, ...)` is convertible to bool.
+template <typename Pred, typename Proj1, typename Proj2, bool kPermute = false>
+class BinaryPredicateProjector {
+ public:
+  constexpr BinaryPredicateProjector(Pred& pred, Proj1& proj1, Proj2& proj2)
+      : pred_(pred), proj1_(proj1), proj2_(proj2) {}
+
+ private:
+  template <typename ProjT, typename ProjU, typename T, typename U>
+  using InvokeResult = invoke_result_t<Pred&,
+                                       invoke_result_t<ProjT&, T&&>,
+                                       invoke_result_t<ProjU&, U&&>>;
+
+  template <typename T, typename U, typename = InvokeResult<Proj1, Proj2, T, U>>
+  constexpr std::pair<Proj1&, Proj2&> GetProjs(priority_tag<3>) const {
+    return {proj1_, proj2_};
+  }
+
+  template <typename T,
+            typename U,
+            bool LazyPermute = kPermute,
+            typename = std::enable_if_t<LazyPermute>,
+            typename = InvokeResult<Proj2, Proj1, T, U>>
+  constexpr std::pair<Proj2&, Proj1&> GetProjs(priority_tag<2>) const {
+    return {proj2_, proj1_};
+  }
+
+  template <typename T,
+            typename U,
+            bool LazyPermute = kPermute,
+            typename = std::enable_if_t<LazyPermute>,
+            typename = InvokeResult<Proj1, Proj1, T, U>>
+  constexpr std::pair<Proj1&, Proj1&> GetProjs(priority_tag<1>) const {
+    return {proj1_, proj1_};
+  }
+
+  template <typename T,
+            typename U,
+            bool LazyPermute = kPermute,
+            typename = std::enable_if_t<LazyPermute>,
+            typename = InvokeResult<Proj2, Proj2, T, U>>
+  constexpr std::pair<Proj2&, Proj2&> GetProjs(priority_tag<0>) const {
+    return {proj2_, proj2_};
+  }
+
+ public:
+  template <typename T, typename U>
+  constexpr bool operator()(T&& lhs, U&& rhs) const {
+    auto projs = GetProjs<T, U>(priority_tag<3>());
+    return gurl_base::invoke(pred_, gurl_base::invoke(projs.first, std::forward<T>(lhs)),
+                        gurl_base::invoke(projs.second, std::forward<U>(rhs)));
+  }
+
+ private:
+  Pred& pred_;
+  Proj1& proj1_;
+  Proj2& proj2_;
+};
+
+// Small wrappers around BinaryPredicateProjector to make the calling side more
+// readable.
+template <typename Pred, typename Proj1, typename Proj2>
+constexpr auto ProjectedBinaryPredicate(Pred& pred,
+                                        Proj1& proj1,
+                                        Proj2& proj2) noexcept {
+  return BinaryPredicateProjector<Pred, Proj1, Proj2>(pred, proj1, proj2);
+}
+
+template <typename Pred, typename Proj1, typename Proj2>
+constexpr auto PermutedProjectedBinaryPredicate(Pred& pred,
+                                                Proj1& proj1,
+                                                Proj2& proj2) noexcept {
+  return BinaryPredicateProjector<Pred, Proj1, Proj2, true>(pred, proj1, proj2);
+}
+
+// This alias is used below to restrict iterator based APIs to types for which
+// `iterator_category` and the pre-increment and post-increment operators are
+// defined. This is required in situations where otherwise an undesired overload
+// would be chosen, e.g. copy_if. In spirit this is similar to C++20's
+// std::input_or_output_iterator, a concept that each iterator should satisfy.
+template <typename Iter,
+          typename = decltype(++std::declval<Iter&>()),
+          typename = decltype(std::declval<Iter&>()++)>
+using iterator_category_t =
+    typename std::iterator_traits<Iter>::iterator_category;
+
+// This alias is used below to restrict range based APIs to types for which
+// `iterator_category_t` is defined for the underlying iterator. This is
+// required in situations where otherwise an undesired overload would be chosen,
+// e.g. transform. In spirit this is similar to C++20's std::ranges::range, a
+// concept that each range should satisfy.
+template <typename Range>
+using range_category_t = iterator_category_t<ranges::iterator_t<Range>>;
+
+}  // namespace internal
+
+namespace ranges {
+
+// C++14 implementation of std::ranges::in_fun_result. Note the because C++14
+// lacks the `no_unique_address` attribute it is commented out.
+//
+// Reference: https://wg21.link/algorithms.results#:~:text=in_fun_result
+template <typename I, typename F>
+struct in_fun_result {
+  /* [[no_unique_address]] */ I in;
+  /* [[no_unique_address]] */ F fun;
+
+  template <typename I2,
+            typename F2,
+            std::enable_if_t<std::is_convertible<const I&, I2>{} &&
+                             std::is_convertible<const F&, F2>{}>>
+  constexpr operator in_fun_result<I2, F2>() const& {
+    return {in, fun};
+  }
+
+  template <typename I2,
+            typename F2,
+            std::enable_if_t<std::is_convertible<I, I2>{} &&
+                             std::is_convertible<F, F2>{}>>
+  constexpr operator in_fun_result<I2, F2>() && {
+    return {std::move(in), std::move(fun)};
+  }
+};
+
+// TODO(crbug.com/1071094): Implement the other result types.
+
+// [alg.nonmodifying] Non-modifying sequence operations
+// Reference: https://wg21.link/alg.nonmodifying
+
+// [alg.all.of] All of
+// Reference: https://wg21.link/alg.all.of
+
+// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.
+//
+// Returns: `false` if `E(i)` is `false` for some iterator `i` in the range
+// `[first, last)`, and `true` otherwise.
+//
+// Complexity: At most `last - first` applications of the predicate and any
+// projection.
+//
+// Reference: https://wg21.link/alg.all.of#:~:text=ranges::all_of(I
+template <typename InputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr bool all_of(InputIterator first,
+                      InputIterator last,
+                      Pred pred,
+                      Proj proj = {}) {
+  for (; first != last; ++first) {
+    if (!gurl_base::invoke(pred, gurl_base::invoke(proj, *first)))
+      return false;
+  }
+
+  return true;
+}
+
+// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.
+//
+// Returns: `false` if `E(i)` is `false` for some iterator `i` in `range`, and
+// `true` otherwise.
+//
+// Complexity: At most `size(range)` applications of the predicate and any
+// projection.
+//
+// Reference: https://wg21.link/alg.all.of#:~:text=ranges::all_of(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr bool all_of(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::all_of(ranges::begin(range), ranges::end(range),
+                        std::move(pred), std::move(proj));
+}
+
+// [alg.any.of] Any of
+// Reference: https://wg21.link/alg.any.of
+
+// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.
+//
+// Returns: `true` if `E(i)` is `true` for some iterator `i` in the range
+// `[first, last)`, and `false` otherwise.
+//
+// Complexity: At most `last - first` applications of the predicate and any
+// projection.
+//
+// Reference: https://wg21.link/alg.any.of#:~:text=ranges::any_of(I
+template <typename InputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr bool any_of(InputIterator first,
+                      InputIterator last,
+                      Pred pred,
+                      Proj proj = {}) {
+  for (; first != last; ++first) {
+    if (gurl_base::invoke(pred, gurl_base::invoke(proj, *first)))
+      return true;
+  }
+
+  return false;
+}
+
+// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.
+//
+// Returns: `true` if `E(i)` is `true` for some iterator `i` in `range`, and
+// `false` otherwise.
+//
+// Complexity: At most `size(range)` applications of the predicate and any
+// projection.
+//
+// Reference: https://wg21.link/alg.any.of#:~:text=ranges::any_of(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr bool any_of(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::any_of(ranges::begin(range), ranges::end(range),
+                        std::move(pred), std::move(proj));
+}
+
+// [alg.none.of] None of
+// Reference: https://wg21.link/alg.none.of
+
+// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.
+//
+// Returns: `false` if `E(i)` is `true` for some iterator `i` in the range
+// `[first, last)`, and `true` otherwise.
+//
+// Complexity: At most `last - first` applications of the predicate and any
+// projection.
+//
+// Reference: https://wg21.link/alg.none.of#:~:text=ranges::none_of(I
+template <typename InputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr bool none_of(InputIterator first,
+                       InputIterator last,
+                       Pred pred,
+                       Proj proj = {}) {
+  for (; first != last; ++first) {
+    if (gurl_base::invoke(pred, gurl_base::invoke(proj, *first)))
+      return false;
+  }
+
+  return true;
+}
+
+// Let `E(i)` be `invoke(pred, invoke(proj, *i))`.
+//
+// Returns: `false` if `E(i)` is `true` for some iterator `i` in `range`, and
+// `true` otherwise.
+//
+// Complexity: At most `size(range)` applications of the predicate and any
+// projection.
+//
+// Reference: https://wg21.link/alg.none.of#:~:text=ranges::none_of(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr bool none_of(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::none_of(ranges::begin(range), ranges::end(range),
+                         std::move(pred), std::move(proj));
+}
+
+// [alg.foreach] For each
+// Reference: https://wg21.link/alg.foreach
+
+// Reference: https://wg21.link/algorithm.syn#:~:text=for_each_result
+template <typename I, typename F>
+using for_each_result = in_fun_result<I, F>;
+
+// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the
+// range `[first, last)`, starting from `first` and proceeding to `last - 1`.
+//
+// Returns: `{last, std::move(f)}`.
+//
+// Complexity: Applies `f` and `proj` exactly `last - first` times.
+//
+// Remarks: If `f` returns a result, the result is ignored.
+//
+// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each(I
+template <typename InputIterator,
+          typename Fun,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr auto for_each(InputIterator first,
+                        InputIterator last,
+                        Fun f,
+                        Proj proj = {}) {
+  for (; first != last; ++first)
+    gurl_base::invoke(f, gurl_base::invoke(proj, *first));
+  return for_each_result<InputIterator, Fun>{first, std::move(f)};
+}
+
+// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the
+// range `range`, starting from `begin(range)` and proceeding to `end(range) -
+// 1`.
+//
+// Returns: `{last, std::move(f)}`.
+//
+// Complexity: Applies `f` and `proj` exactly `size(range)` times.
+//
+// Remarks: If `f` returns a result, the result is ignored.
+//
+// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each(R
+template <typename Range,
+          typename Fun,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto for_each(Range&& range, Fun f, Proj proj = {}) {
+  return ranges::for_each(ranges::begin(range), ranges::end(range),
+                          std::move(f), std::move(proj));
+}
+
+// Reference: https://wg21.link/algorithm.syn#:~:text=for_each_n_result
+template <typename I, typename F>
+using for_each_n_result = in_fun_result<I, F>;
+
+// Preconditions: `n >= 0` is `true`.
+//
+// Effects: Calls `invoke(f, invoke(proj, *i))` for every iterator `i` in the
+// range `[first, first + n)` in order.
+//
+// Returns: `{first + n, std::move(f)}`.
+//
+// Remarks: If `f` returns a result, the result is ignored.
+//
+// Reference: https://wg21.link/alg.foreach#:~:text=ranges::for_each_n
+template <typename InputIterator,
+          typename Size,
+          typename Fun,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr auto for_each_n(InputIterator first, Size n, Fun f, Proj proj = {}) {
+  while (n > 0) {
+    gurl_base::invoke(f, gurl_base::invoke(proj, *first));
+    ++first;
+    --n;
+  }
+
+  return for_each_n_result<InputIterator, Fun>{first, std::move(f)};
+}
+
+// [alg.find] Find
+// Reference: https://wg21.link/alg.find
+
+// Let `E(i)` be `bool(invoke(proj, *i) == value)`.
+//
+// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)`
+// is `true`. Returns `last` if no such iterator is found.
+//
+// Complexity: At most `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.find#:~:text=ranges::find(I
+template <typename InputIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr auto find(InputIterator first,
+                    InputIterator last,
+                    const T& value,
+                    Proj proj = {}) {
+  // Note: In order to be able to apply `proj` to each element in [first, last)
+  // we are dispatching to std::find_if instead of std::find.
+  return std::find_if(first, last, [&proj, &value](auto&& lhs) {
+    return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value;
+  });
+}
+
+// Let `E(i)` be `bool(invoke(proj, *i) == value)`.
+//
+// Returns: The first iterator `i` in `range` for which `E(i)` is `true`.
+// Returns `end(range)` if no such iterator is found.
+//
+// Complexity: At most `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.find#:~:text=ranges::find(R
+template <typename Range,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto find(Range&& range, const T& value, Proj proj = {}) {
+  return ranges::find(ranges::begin(range), ranges::end(range), value,
+                      std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)`
+// is `true`. Returns `last` if no such iterator is found.
+//
+// Complexity: At most `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if(I
+template <typename InputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr auto find_if(InputIterator first,
+                       InputIterator last,
+                       Pred pred,
+                       Proj proj = {}) {
+  return std::find_if(first, last,
+                      internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Returns: The first iterator `i` in `range` for which `E(i)` is `true`.
+// Returns `end(range)` if no such iterator is found.
+//
+// Complexity: At most `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto find_if(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::find_if(ranges::begin(range), ranges::end(range),
+                         std::move(pred), std::move(proj));
+}
+
+// Let `E(i)` be `bool(!invoke(pred, invoke(proj, *i)))`.
+//
+// Returns: The first iterator `i` in the range `[first, last)` for which `E(i)`
+// is `true`. Returns `last` if no such iterator is found.
+//
+// Complexity: At most `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if_not(I
+template <typename InputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr auto find_if_not(InputIterator first,
+                           InputIterator last,
+                           Pred pred,
+                           Proj proj = {}) {
+  return std::find_if_not(first, last,
+                          internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(i)` be `bool(!invoke(pred, invoke(proj, *i)))`.
+//
+// Returns: The first iterator `i` in `range` for which `E(i)` is `true`.
+// Returns `end(range)` if no such iterator is found.
+//
+// Complexity: At most `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.find#:~:text=ranges::find_if_not(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto find_if_not(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::find_if_not(ranges::begin(range), ranges::end(range),
+                             std::move(pred), std::move(proj));
+}
+
+// [alg.find.end] Find end
+// Reference: https://wg21.link/alg.find.end
+
+// Let:
+// - `E(i,n)` be `invoke(pred, invoke(proj1, *(i + n)),
+//                             invoke(proj2, *(first2 + n)))`
+//
+// - `i` be `last1` if `[first2, last2)` is empty, or if
+//   `(last2 - first2) > (last1 - first1)` is `true`, or if there is no iterator
+//   in the range `[first1, last1 - (last2 - first2))` such that for every
+//   non-negative integer `n < (last2 - first2)`, `E(i,n)` is `true`. Otherwise
+//   `i` is the last such iterator in `[first1, last1 - (last2 - first2))`.
+//
+// Returns: `i`
+// Note: std::ranges::find_end(I1 first1,...) returns a range, rather than an
+// iterator. For simplicitly we match std::find_end's return type instead.
+//
+// Complexity:
+// At most `(last2 - first2) * (last1 - first1 - (last2 - first2) + 1)`
+// applications of the corresponding predicate and any projections.
+//
+// Reference: https://wg21.link/alg.find.end#:~:text=ranges::find_end(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>>
+constexpr auto find_end(ForwardIterator1 first1,
+                        ForwardIterator1 last1,
+                        ForwardIterator2 first2,
+                        ForwardIterator2 last2,
+                        Pred pred = {},
+                        Proj1 proj1 = {},
+                        Proj2 proj2 = {}) {
+  return std::find_end(first1, last1, first2, last2,
+                       internal::ProjectedBinaryPredicate(pred, proj1, proj2));
+}
+
+// Let:
+// - `E(i,n)` be `invoke(pred, invoke(proj1, *(i + n)),
+//                             invoke(proj2, *(first2 + n)))`
+//
+// - `i` be `end(range1)` if `range2` is empty, or if
+//   `size(range2) > size(range1)` is `true`, or if there is no iterator in the
+//   range `[begin(range1), end(range1) - size(range2))` such that for every
+//   non-negative integer `n < size(range2)`, `E(i,n)` is `true`. Otherwise `i`
+//   is the last such iterator in `[begin(range1), end(range1) - size(range2))`.
+//
+// Returns: `i`
+// Note: std::ranges::find_end(R1&& r1,...) returns a range, rather than an
+// iterator. For simplicitly we match std::find_end's return type instead.
+//
+// Complexity: At most `size(range2) * (size(range1) - size(range2) + 1)`
+// applications of the corresponding predicate and any projections.
+//
+// Reference: https://wg21.link/alg.find.end#:~:text=ranges::find_end(R1
+template <typename Range1,
+          typename Range2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>>
+constexpr auto find_end(Range1&& range1,
+                        Range2&& range2,
+                        Pred pred = {},
+                        Proj1 proj1 = {},
+                        Proj2 proj2 = {}) {
+  return ranges::find_end(ranges::begin(range1), ranges::end(range1),
+                          ranges::begin(range2), ranges::end(range2),
+                          std::move(pred), std::move(proj1), std::move(proj2));
+}
+
+// [alg.find.first.of] Find first
+// Reference: https://wg21.link/alg.find.first.of
+
+// Let `E(i,j)` be `bool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))`.
+//
+// Effects: Finds an element that matches one of a set of values.
+//
+// Returns: The first iterator `i` in the range `[first1, last1)` such that for
+// some iterator `j` in the range `[first2, last2)` `E(i,j)` holds. Returns
+// `last1` if `[first2, last2)` is empty or if no such iterator is found.
+//
+// Complexity: At most `(last1 - first1) * (last2 - first2)` applications of the
+// corresponding predicate and any projections.
+//
+// Reference:
+// https://wg21.link/alg.find.first.of#:~:text=ranges::find_first_of(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>>
+constexpr auto find_first_of(ForwardIterator1 first1,
+                             ForwardIterator1 last1,
+                             ForwardIterator2 first2,
+                             ForwardIterator2 last2,
+                             Pred pred = {},
+                             Proj1 proj1 = {},
+                             Proj2 proj2 = {}) {
+  return std::find_first_of(
+      first1, last1, first2, last2,
+      internal::ProjectedBinaryPredicate(pred, proj1, proj2));
+}
+
+// Let `E(i,j)` be `bool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))`.
+//
+// Effects: Finds an element that matches one of a set of values.
+//
+// Returns: The first iterator `i` in `range1` such that for some iterator `j`
+// in `range2` `E(i,j)` holds. Returns `end(range1)` if `range2` is empty or if
+// no such iterator is found.
+//
+// Complexity: At most `size(range1) * size(range2)` applications of the
+// corresponding predicate and any projections.
+//
+// Reference:
+// https://wg21.link/alg.find.first.of#:~:text=ranges::find_first_of(R1
+template <typename Range1,
+          typename Range2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>>
+constexpr auto find_first_of(Range1&& range1,
+                             Range2&& range2,
+                             Pred pred = {},
+                             Proj1 proj1 = {},
+                             Proj2 proj2 = {}) {
+  return ranges::find_first_of(
+      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),
+      ranges::end(range2), std::move(pred), std::move(proj1), std::move(proj2));
+}
+
+// [alg.adjacent.find] Adjacent find
+// Reference: https://wg21.link/alg.adjacent.find
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`.
+//
+// Returns: The first iterator `i` such that both `i` and `i + 1` are in the
+// range `[first, last)` for which `E(i)` holds. Returns `last` if no such
+// iterator is found.
+//
+// Complexity: Exactly `min((i - first) + 1, (last - first) - 1)` applications
+// of the corresponding predicate, where `i` is `adjacent_find`'s return value.
+//
+// Reference:
+// https://wg21.link/alg.adjacent.find#:~:text=ranges::adjacent_find(I
+template <typename ForwardIterator,
+          typename Pred = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto adjacent_find(ForwardIterator first,
+                             ForwardIterator last,
+                             Pred pred = {},
+                             Proj proj = {}) {
+  // Implementation inspired by cppreference.com:
+  // https://en.cppreference.com/w/cpp/algorithm/adjacent_find
+  //
+  // A reimplementation is required, because std::adjacent_find is not constexpr
+  // prior to C++20. Once we have C++20, we should switch to standard library
+  // implementation.
+  if (first == last)
+    return last;
+
+  for (ForwardIterator next = first; ++next != last; ++first) {
+    if (gurl_base::invoke(pred, gurl_base::invoke(proj, *first),
+                     gurl_base::invoke(proj, *next))) {
+      return first;
+    }
+  }
+
+  return last;
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))`.
+//
+// Returns: The first iterator `i` such that both `i` and `i + 1` are in the
+// range `range` for which `E(i)` holds. Returns `end(range)` if no such
+// iterator is found.
+//
+// Complexity: Exactly `min((i - begin(range)) + 1, size(range) - 1)`
+// applications of the corresponding predicate, where `i` is `adjacent_find`'s
+// return value.
+//
+// Reference:
+// https://wg21.link/alg.adjacent.find#:~:text=ranges::adjacent_find(R
+template <typename Range,
+          typename Pred = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto adjacent_find(Range&& range, Pred pred = {}, Proj proj = {}) {
+  return ranges::adjacent_find(ranges::begin(range), ranges::end(range),
+                               std::move(pred), std::move(proj));
+}
+
+// [alg.count] Count
+// Reference: https://wg21.link/alg.count
+
+// Let `E(i)` be `invoke(proj, *i) == value`.
+//
+// Effects: Returns the number of iterators `i` in the range `[first, last)` for
+// which `E(i)` holds.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.count#:~:text=ranges::count(I
+template <typename InputIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr auto count(InputIterator first,
+                     InputIterator last,
+                     const T& value,
+                     Proj proj = {}) {
+  // Note: In order to be able to apply `proj` to each element in [first, last)
+  // we are dispatching to std::count_if instead of std::count.
+  return std::count_if(first, last, [&proj, &value](auto&& lhs) {
+    return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value;
+  });
+}
+
+// Let `E(i)` be `invoke(proj, *i) == value`.
+//
+// Effects: Returns the number of iterators `i` in `range` for which `E(i)`
+// holds.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.count#:~:text=ranges::count(R
+template <typename Range,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto count(Range&& range, const T& value, Proj proj = {}) {
+  return ranges::count(ranges::begin(range), ranges::end(range), value,
+                       std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Effects: Returns the number of iterators `i` in the range `[first, last)` for
+// which `E(i)` holds.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.count#:~:text=ranges::count_if(I
+template <typename InputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>>
+constexpr auto count_if(InputIterator first,
+                        InputIterator last,
+                        Pred pred,
+                        Proj proj = {}) {
+  return std::count_if(first, last,
+                       internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Effects: Returns the number of iterators `i` in `range` for which `E(i)`
+// holds.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.count#:~:text=ranges::count_if(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto count_if(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::count_if(ranges::begin(range), ranges::end(range),
+                          std::move(pred), std::move(proj));
+}
+
+// [mismatch] Mismatch
+// Reference: https://wg21.link/mismatch
+
+// Let `E(n)` be `!invoke(pred, invoke(proj1, *(first1 + n)),
+//                              invoke(proj2, *(first2 + n)))`.
+//
+// Let `N` be `min(last1 - first1, last2 - first2)`.
+//
+// Returns: `{ first1 + n, first2 + n }`, where `n` is the smallest integer in
+// `[0, N)` such that `E(n)` holds, or `N` if no such integer exists.
+//
+// Complexity: At most `N` applications of the corresponding predicate and any
+// projections.
+//
+// Reference: https://wg21.link/mismatch#:~:text=ranges::mismatch(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>>
+constexpr auto mismatch(ForwardIterator1 first1,
+                        ForwardIterator1 last1,
+                        ForwardIterator2 first2,
+                        ForwardIterator2 last2,
+                        Pred pred = {},
+                        Proj1 proj1 = {},
+                        Proj2 proj2 = {}) {
+  return std::mismatch(first1, last1, first2, last2,
+                       internal::ProjectedBinaryPredicate(pred, proj1, proj2));
+}
+
+// Let `E(n)` be `!invoke(pred, invoke(proj1, *(begin(range1) + n)),
+//                              invoke(proj2, *(begin(range2) + n)))`.
+//
+// Let `N` be `min(size(range1), size(range2))`.
+//
+// Returns: `{ begin(range1) + n, begin(range2) + n }`, where `n` is the
+// smallest integer in `[0, N)` such that `E(n)` holds, or `N` if no such
+// integer exists.
+//
+// Complexity: At most `N` applications of the corresponding predicate and any
+// projections.
+//
+// Reference: https://wg21.link/mismatch#:~:text=ranges::mismatch(R1
+template <typename Range1,
+          typename Range2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>>
+constexpr auto mismatch(Range1&& range1,
+                        Range2&& range2,
+                        Pred pred = {},
+                        Proj1 proj1 = {},
+                        Proj2 proj2 = {}) {
+  return ranges::mismatch(ranges::begin(range1), ranges::end(range1),
+                          ranges::begin(range2), ranges::end(range2),
+                          std::move(pred), std::move(proj1), std::move(proj2));
+}
+
+// [alg.equal] Equal
+// Reference: https://wg21.link/alg.equal
+
+// Let `E(i)` be
+//   `invoke(pred, invoke(proj1, *i), invoke(proj2, *(first2 + (i - first1))))`.
+//
+// Returns: If `last1 - first1 != last2 - first2`, return `false.` Otherwise
+// return `true` if `E(i)` holds for every iterator `i` in the range `[first1,
+// last1)`. Otherwise, returns `false`.
+//
+// Complexity: If the types of `first1`, `last1`, `first2`, and `last2` meet the
+// `RandomAccessIterator` requirements and `last1 - first1 != last2 - first2`,
+// then no applications of the corresponding predicate and each projection;
+// otherwise, at most `min(last1 - first1, last2 - first2)` applications of the
+// corresponding predicate and any projections.
+//
+// Reference: https://wg21.link/alg.equal#:~:text=ranges::equal(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>>
+constexpr bool equal(ForwardIterator1 first1,
+                     ForwardIterator1 last1,
+                     ForwardIterator2 first2,
+                     ForwardIterator2 last2,
+                     Pred pred = {},
+                     Proj1 proj1 = {},
+                     Proj2 proj2 = {}) {
+  return std::equal(first1, last1, first2, last2,
+                    internal::ProjectedBinaryPredicate(pred, proj1, proj2));
+}
+
+// Let `E(i)` be
+//   `invoke(pred, invoke(proj1, *i),
+//                 invoke(proj2, *(begin(range2) + (i - begin(range1)))))`.
+//
+// Returns: If `size(range1) != size(range2)`, return `false.` Otherwise return
+// `true` if `E(i)` holds for every iterator `i` in `range1`. Otherwise, returns
+// `false`.
+//
+// Complexity: If the types of `begin(range1)`, `end(range1)`, `begin(range2)`,
+// and `end(range2)` meet the `RandomAccessIterator` requirements and
+// `size(range1) != size(range2)`, then no applications of the corresponding
+// predicate and each projection;
+// otherwise, at most `min(size(range1), size(range2))` applications of the
+// corresponding predicate and any projections.
+//
+// Reference: https://wg21.link/alg.equal#:~:text=ranges::equal(R1
+template <typename Range1,
+          typename Range2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>>
+constexpr bool equal(Range1&& range1,
+                     Range2&& range2,
+                     Pred pred = {},
+                     Proj1 proj1 = {},
+                     Proj2 proj2 = {}) {
+  return ranges::equal(ranges::begin(range1), ranges::end(range1),
+                       ranges::begin(range2), ranges::end(range2),
+                       std::move(pred), std::move(proj1), std::move(proj2));
+}
+
+// [alg.is.permutation] Is permutation
+// Reference: https://wg21.link/alg.is.permutation
+
+// Returns: If `last1 - first1 != last2 - first2`, return `false`. Otherwise
+// return `true` if there exists a permutation of the elements in the range
+// `[first2, last2)`, bounded by `[pfirst, plast)`, such that
+// `ranges::equal(first1, last1, pfirst, plast, pred, proj, proj)` returns
+// `true`; otherwise, returns `false`.
+//
+// Complexity: No applications of the corresponding predicate if
+// ForwardIterator1 and ForwardIterator2 meet the requirements of random access
+// iterators and `last1 - first1 != last2 - first2`. Otherwise, exactly
+// `last1 - first1` applications of the corresponding predicate and projections
+// if `ranges::equal(first1, last1, first2, last2, pred, proj, proj)` would
+// return true;
+// otherwise, at worst `O(N^2)`, where `N` has the value `last1 - first1`.
+//
+// Reference:
+// https://wg21.link/alg.is.permutation#:~:text=ranges::is_permutation(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>>
+constexpr bool is_permutation(ForwardIterator1 first1,
+                              ForwardIterator1 last1,
+                              ForwardIterator2 first2,
+                              ForwardIterator2 last2,
+                              Pred pred = {},
+                              Proj1 proj1 = {},
+                              Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::is_permutation expects
+  // pred(proj1(lhs), proj1(rhs)) to compile.
+  return std::is_permutation(
+      first1, last1, first2, last2,
+      internal::PermutedProjectedBinaryPredicate(pred, proj1, proj2));
+}
+
+// Returns: If `size(range1) != size(range2)`, return `false`. Otherwise return
+// `true` if there exists a permutation of the elements in `range2`, bounded by
+// `[pbegin, pend)`, such that
+// `ranges::equal(range1, [pbegin, pend), pred, proj, proj)` returns `true`;
+// otherwise, returns `false`.
+//
+// Complexity: No applications of the corresponding predicate if Range1 and
+// Range2 meet the requirements of random access ranges and
+// `size(range1) != size(range2)`. Otherwise, exactly `size(range1)`
+// applications of the corresponding predicate and projections if
+// `ranges::equal(range1, range2, pred, proj, proj)` would return true;
+// otherwise, at worst `O(N^2)`, where `N` has the value `size(range1)`.
+//
+// Reference:
+// https://wg21.link/alg.is.permutation#:~:text=ranges::is_permutation(R1
+template <typename Range1,
+          typename Range2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>>
+constexpr bool is_permutation(Range1&& range1,
+                              Range2&& range2,
+                              Pred pred = {},
+                              Proj1 proj1 = {},
+                              Proj2 proj2 = {}) {
+  return ranges::is_permutation(
+      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),
+      ranges::end(range2), std::move(pred), std::move(proj1), std::move(proj2));
+}
+
+// [alg.search] Search
+// Reference: https://wg21.link/alg.search
+
+// Returns: `i`, where `i` is the first iterator in the range
+// `[first1, last1 - (last2 - first2))` such that for every non-negative integer
+// `n` less than `last2 - first2` the condition
+// `bool(invoke(pred, invoke(proj1, *(i + n)), invoke(proj2, *(first2 + n))))`
+// is `true`.
+// Returns `last1` if no such iterator exists.
+// Note: std::ranges::search(I1 first1,...) returns a range, rather than an
+// iterator. For simplicitly we match std::search's return type instead.
+//
+// Complexity: At most `(last1 - first1) * (last2 - first2)` applications of the
+// corresponding predicate and projections.
+//
+// Reference: https://wg21.link/alg.search#:~:text=ranges::search(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>>
+constexpr auto search(ForwardIterator1 first1,
+                      ForwardIterator1 last1,
+                      ForwardIterator2 first2,
+                      ForwardIterator2 last2,
+                      Pred pred = {},
+                      Proj1 proj1 = {},
+                      Proj2 proj2 = {}) {
+  return std::search(first1, last1, first2, last2,
+                     internal::ProjectedBinaryPredicate(pred, proj1, proj2));
+}
+
+// Returns: `i`, where `i` is the first iterator in the range
+// `[begin(range1), end(range1) - size(range2))` such that for every
+// non-negative integer `n` less than `size(range2)` the condition
+// `bool(invoke(pred, invoke(proj1, *(i + n)),
+//                    invoke(proj2, *(begin(range2) + n))))` is `true`.
+// Returns `end(range1)` if no such iterator exists.
+// Note: std::ranges::search(R1&& r1,...) returns a range, rather than an
+// iterator. For simplicitly we match std::search's return type instead.
+//
+// Complexity: At most `size(range1) * size(range2)` applications of the
+// corresponding predicate and projections.
+//
+// Reference: https://wg21.link/alg.search#:~:text=ranges::search(R1
+template <typename Range1,
+          typename Range2,
+          typename Pred = ranges::equal_to,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Pred&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>>
+constexpr auto search(Range1&& range1,
+                      Range2&& range2,
+                      Pred pred = {},
+                      Proj1 proj1 = {},
+                      Proj2 proj2 = {}) {
+  return ranges::search(ranges::begin(range1), ranges::end(range1),
+                        ranges::begin(range2), ranges::end(range2),
+                        std::move(pred), std::move(proj1), std::move(proj2));
+}
+
+// Mandates: The type `Size` is convertible to an integral type.
+//
+// Returns: `i` where `i` is the first iterator in the range
+// `[first, last - count)` such that for every non-negative integer `n` less
+// than `count`, the following condition holds:
+// `invoke(pred, invoke(proj, *(i + n)), value)`.
+// Returns `last` if no such iterator is found.
+// Note: std::ranges::search_n(I1 first1,...) returns a range, rather than an
+// iterator. For simplicitly we match std::search_n's return type instead.
+//
+// Complexity: At most `last - first` applications of the corresponding
+// predicate and projection.
+//
+// Reference: https://wg21.link/alg.search#:~:text=ranges::search_n(I
+template <typename ForwardIterator,
+          typename Size,
+          typename T,
+          typename Pred = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto search_n(ForwardIterator first,
+                        ForwardIterator last,
+                        Size count,
+                        const T& value,
+                        Pred pred = {},
+                        Proj proj = {}) {
+  // The second arg is guaranteed to be `value`, so we'll simply apply the
+  // identity projection.
+  identity value_proj;
+  return std::search_n(
+      first, last, count, value,
+      internal::ProjectedBinaryPredicate(pred, proj, value_proj));
+}
+
+// Mandates: The type `Size` is convertible to an integral type.
+//
+// Returns: `i` where `i` is the first iterator in the range
+// `[begin(range), end(range) - count)` such that for every non-negative integer
+// `n` less than `count`, the following condition holds:
+// `invoke(pred, invoke(proj, *(i + n)), value)`.
+// Returns `end(arnge)` if no such iterator is found.
+// Note: std::ranges::search_n(R1&& r1,...) returns a range, rather than an
+// iterator. For simplicitly we match std::search_n's return type instead.
+//
+// Complexity: At most `size(range)` applications of the corresponding predicate
+// and projection.
+//
+// Reference: https://wg21.link/alg.search#:~:text=ranges::search_n(R
+template <typename Range,
+          typename Size,
+          typename T,
+          typename Pred = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto search_n(Range&& range,
+                        Size count,
+                        const T& value,
+                        Pred pred = {},
+                        Proj proj = {}) {
+  return ranges::search_n(ranges::begin(range), ranges::end(range), count,
+                          value, std::move(pred), std::move(proj));
+}
+
+// [alg.modifying.operations] Mutating sequence operations
+// Reference: https://wg21.link/alg.modifying.operations
+
+// [alg.copy] Copy
+// Reference: https://wg21.link/alg.copy
+
+// Let N be `last - first`.
+//
+// Preconditions: `result` is not in the range `[first, last)`.
+//
+// Effects: Copies elements in the range `[first, last)` into the range
+// `[result, result + N)` starting from `first` and proceeding to `last`. For
+// each non-negative integer `n < N` , performs `*(result + n) = *(first + n)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto copy(InputIterator first,
+                    InputIterator last,
+                    OutputIterator result) {
+  return std::copy(first, last, result);
+}
+
+// Let N be `size(range)`.
+//
+// Preconditions: `result` is not in `range`.
+//
+// Effects: Copies elements in `range` into the range `[result, result + N)`
+// starting from `begin(range)` and proceeding to `end(range)`. For each
+// non-negative integer `n < N` , performs
+// *(result + n) = *(begin(range) + n)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy(R
+template <typename Range,
+          typename OutputIterator,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto copy(Range&& range, OutputIterator result) {
+  return ranges::copy(ranges::begin(range), ranges::end(range), result);
+}
+
+// Let `N` be `max(0, n)`.
+//
+// Mandates: The type `Size` is convertible to an integral type.
+//
+// Effects: For each non-negative integer `i < N`, performs
+// `*(result + i) = *(first + i)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_n
+template <typename InputIterator,
+          typename Size,
+          typename OutputIterator,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto copy_n(InputIterator first, Size n, OutputIterator result) {
+  return std::copy_n(first, n, result);
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`, and `N` be the number
+// of iterators `i` in the range `[first, last)` for which the condition `E(i)`
+// holds.
+//
+// Preconditions: The ranges `[first, last)` and
+// `[result, result + (last - first))` do not overlap.
+//
+// Effects: Copies all of the elements referred to by the iterator `i` in the
+// range `[first, last)` for which `E(i)` is true.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_if(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto copy_if(InputIterator first,
+                       InputIterator last,
+                       OutputIterator result,
+                       Pred pred,
+                       Proj proj = {}) {
+  return std::copy_if(first, last, result,
+                      internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`, and `N` be the number
+// of iterators `i` in `range` for which the condition `E(i)` holds.
+//
+// Preconditions: `range`  and `[result, result + size(range))` do not overlap.
+//
+// Effects: Copies all of the elements referred to by the iterator `i` in
+// `range` for which `E(i)` is true.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_if(R
+template <typename Range,
+          typename OutputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto copy_if(Range&& range,
+                       OutputIterator result,
+                       Pred pred,
+                       Proj proj = {}) {
+  return ranges::copy_if(ranges::begin(range), ranges::end(range), result,
+                         std::move(pred), std::move(proj));
+}
+
+// Let `N` be `last - first`.
+//
+// Preconditions: `result` is not in the range `(first, last]`.
+//
+// Effects: Copies elements in the range `[first, last)` into the range
+// `[result - N, result)` starting from `last - 1` and proceeding to `first`.
+// For each positive integer `n ≤ N`, performs `*(result - n) = *(last - n)`.
+//
+// Returns: `result - N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_backward(I1
+template <typename BidirectionalIterator1,
+          typename BidirectionalIterator2,
+          typename = internal::iterator_category_t<BidirectionalIterator1>,
+          typename = internal::iterator_category_t<BidirectionalIterator2>>
+constexpr auto copy_backward(BidirectionalIterator1 first,
+                             BidirectionalIterator1 last,
+                             BidirectionalIterator2 result) {
+  return std::copy_backward(first, last, result);
+}
+
+// Let `N` be `size(range)`.
+//
+// Preconditions: `result` is not in the range `(begin(range), end(range)]`.
+//
+// Effects: Copies elements in `range` into the range `[result - N, result)`
+// starting from `end(range) - 1` and proceeding to `begin(range)`. For each
+// positive integer `n ≤ N`, performs `*(result - n) = *(end(range) - n)`.
+//
+// Returns: `result - N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.copy#:~:text=ranges::copy_backward(R
+template <typename Range,
+          typename BidirectionalIterator,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<BidirectionalIterator>>
+constexpr auto copy_backward(Range&& range, BidirectionalIterator result) {
+  return ranges::copy_backward(ranges::begin(range), ranges::end(range),
+                               result);
+}
+
+// [alg.move] Move
+// Reference: https://wg21.link/alg.move
+
+// Let `E(n)` be `std::move(*(first + n))`.
+//
+// Let `N` be `last - first`.
+//
+// Preconditions: `result` is not in the range `[first, last)`.
+//
+// Effects: Moves elements in the range `[first, last)` into the range `[result,
+// result + N)` starting from `first` and proceeding to `last`. For each
+// non-negative integer `n < N`, performs `*(result + n) = E(n)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.move#:~:text=ranges::move(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto move(InputIterator first,
+                    InputIterator last,
+                    OutputIterator result) {
+  return std::move(first, last, result);
+}
+
+// Let `E(n)` be `std::move(*(begin(range) + n))`.
+//
+// Let `N` be `size(range)`.
+//
+// Preconditions: `result` is not in `range`.
+//
+// Effects: Moves elements in `range` into the range `[result, result + N)`
+// starting from `begin(range)` and proceeding to `end(range)`. For each
+// non-negative integer `n < N`, performs `*(result + n) = E(n)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.move#:~:text=ranges::move(R
+template <typename Range,
+          typename OutputIterator,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto move(Range&& range, OutputIterator result) {
+  return ranges::move(ranges::begin(range), ranges::end(range), result);
+}
+
+// Let `E(n)` be `std::move(*(last - n))`.
+//
+// Let `N` be `last - first`.
+//
+// Preconditions: `result` is not in the range `(first, last]`.
+//
+// Effects: Moves elements in the range `[first, last)` into the range
+// `[result - N, result)` starting from `last - 1` and proceeding to `first`.
+// For each positive integer `n ≤ N`, performs `*(result - n) = E(n)`.
+//
+// Returns: `result - N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.move#:~:text=ranges::move_backward(I1
+template <typename BidirectionalIterator1,
+          typename BidirectionalIterator2,
+          typename = internal::iterator_category_t<BidirectionalIterator1>,
+          typename = internal::iterator_category_t<BidirectionalIterator2>>
+constexpr auto move_backward(BidirectionalIterator1 first,
+                             BidirectionalIterator1 last,
+                             BidirectionalIterator2 result) {
+  return std::move_backward(first, last, result);
+}
+
+// Let `E(n)` be `std::move(*(end(range) - n))`.
+//
+// Let `N` be `size(range)`.
+//
+// Preconditions: `result` is not in the range `(begin(range), end(range)]`.
+//
+// Effects: Moves elements in `range` into the range `[result - N, result)`
+// starting from `end(range) - 1` and proceeding to `begin(range)`. For each
+// positive integer `n ≤ N`, performs `*(result - n) = E(n)`.
+//
+// Returns: `result - N`
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.move#:~:text=ranges::move_backward(R
+template <typename Range,
+          typename BidirectionalIterator,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<BidirectionalIterator>>
+constexpr auto move_backward(Range&& range, BidirectionalIterator result) {
+  return ranges::move_backward(ranges::begin(range), ranges::end(range),
+                               result);
+}
+
+// [alg.swap] Swap
+// Reference: https://wg21.link/alg.swap
+
+// Let `M` be `min(last1 - first1, last2 - first2)`.
+//
+// Preconditions: The two ranges `[first1, last1)` and `[first2, last2)` do not
+// overlap. `*(first1 + n)` is swappable with `*(first2 + n)`.
+//
+// Effects: For each non-negative integer `n < M` performs
+// `swap(*(first1 + n), *(first2 + n))`
+//
+// Returns: `first2 + M`
+//
+// Complexity: Exactly `M` swaps.
+//
+// Reference: https://wg21.link/alg.swap#:~:text=ranges::swap_ranges(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>>
+constexpr auto swap_ranges(ForwardIterator1 first1,
+                           ForwardIterator1 last1,
+                           ForwardIterator2 first2,
+                           ForwardIterator2 last2) {
+  // std::swap_ranges does not have a `last2` overload. Thus we need to
+  // adjust `last1` to ensure to not read past `last2`.
+  last1 = std::next(first1, std::min(std::distance(first1, last1),
+                                     std::distance(first2, last2)));
+  return std::swap_ranges(first1, last1, first2);
+}
+
+// Let `M` be `min(size(range1), size(range2))`.
+//
+// Preconditions: The two ranges `range1` and `range2` do not overlap.
+// `*(begin(range1) + n)` is swappable with `*(begin(range2) + n)`.
+//
+// Effects: For each non-negative integer `n < M` performs
+// `swap(*(begin(range1) + n), *(begin(range2) + n))`
+//
+// Returns: `begin(range2) + M`
+//
+// Complexity: Exactly `M` swaps.
+//
+// Reference: https://wg21.link/alg.swap#:~:text=ranges::swap_ranges(R1
+template <typename Range1,
+          typename Range2,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>>
+constexpr auto swap_ranges(Range1&& range1, Range2&& range2) {
+  return ranges::swap_ranges(ranges::begin(range1), ranges::end(range1),
+                             ranges::begin(range2), ranges::end(range2));
+}
+
+// [alg.transform] Transform
+// Reference: https://wg21.link/alg.transform
+
+// Let `N` be `last1 - first1`,
+// `E(i)` be `invoke(op, invoke(proj, *(first1 + (i - result))))`.
+//
+// Preconditions: `op` does not invalidate iterators or subranges, nor modify
+// elements in the ranges `[first1, first1 + N]`, and `[result, result + N]`.
+//
+// Effects: Assigns through every iterator `i` in the range
+// `[result, result + N)` a new corresponding value equal to `E(i)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` applications of `op` and any projections.
+//
+// Remarks: result may be equal to `first1`.
+//
+// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename UnaryOperation,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<UnaryOperation&,
+                                       projected<InputIterator, Proj>>>
+constexpr auto transform(InputIterator first1,
+                         InputIterator last1,
+                         OutputIterator result,
+                         UnaryOperation op,
+                         Proj proj = {}) {
+  return std::transform(first1, last1, result, [&op, &proj](auto&& arg) {
+    return gurl_base::invoke(op,
+                        gurl_base::invoke(proj, std::forward<decltype(arg)>(arg)));
+  });
+}
+
+// Let `N` be `size(range)`,
+// `E(i)` be `invoke(op, invoke(proj, *(begin(range) + (i - result))))`.
+//
+// Preconditions: `op` does not invalidate iterators or subranges, nor modify
+// elements in the ranges `[begin(range), end(range)]`, and
+// `[result, result + N]`.
+//
+// Effects: Assigns through every iterator `i` in the range
+// `[result, result + N)` a new corresponding value equal to `E(i)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` applications of `op` and any projections.
+//
+// Remarks: result may be equal to `begin(range)`.
+//
+// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(R
+template <typename Range,
+          typename OutputIterator,
+          typename UnaryOperation,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<UnaryOperation&,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto transform(Range&& range,
+                         OutputIterator result,
+                         UnaryOperation op,
+                         Proj proj = {}) {
+  return ranges::transform(ranges::begin(range), ranges::end(range), result,
+                           std::move(op), std::move(proj));
+}
+
+// Let:
+// `N` be `min(last1 - first1, last2 - first2)`,
+// `E(i)` be `invoke(binary_op, invoke(proj1, *(first1 + (i - result))),
+//                              invoke(proj2, *(first2 + (i - result))))`.
+//
+// Preconditions: `binary_op` does not invalidate iterators or subranges, nor
+// modify elements in the ranges `[first1, first1 + N]`, `[first2, first2 + N]`,
+// and `[result, result + N]`.
+//
+// Effects: Assigns through every iterator `i` in the range
+// `[result, result + N)` a new corresponding value equal to `E(i)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` applications of `binary_op`, and any projections.
+//
+// Remarks: `result` may be equal to `first1` or `first2`.
+//
+// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename OutputIterator,
+          typename BinaryOperation,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<BinaryOperation&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>>
+constexpr auto transform(ForwardIterator1 first1,
+                         ForwardIterator1 last1,
+                         ForwardIterator2 first2,
+                         ForwardIterator2 last2,
+                         OutputIterator result,
+                         BinaryOperation binary_op,
+                         Proj1 proj1 = {},
+                         Proj2 proj2 = {}) {
+  // std::transform does not have a `last2` overload. Thus we need to adjust
+  // `last1` to ensure to not read past `last2`.
+  last1 = std::next(first1, std::min(std::distance(first1, last1),
+                                     std::distance(first2, last2)));
+  return std::transform(
+      first1, last1, first2, result,
+      [&binary_op, &proj1, &proj2](auto&& lhs, auto&& rhs) {
+        return gurl_base::invoke(
+            binary_op, gurl_base::invoke(proj1, std::forward<decltype(lhs)>(lhs)),
+            gurl_base::invoke(proj2, std::forward<decltype(rhs)>(rhs)));
+      });
+}
+
+// Let:
+// `N` be `min(size(range1), size(range2)`,
+// `E(i)` be `invoke(binary_op, invoke(proj1, *(begin(range1) + (i - result))),
+//                              invoke(proj2, *(begin(range2) + (i - result))))`
+//
+// Preconditions: `binary_op` does not invalidate iterators or subranges, nor
+// modify elements in the ranges `[begin(range1), end(range1)]`,
+// `[begin(range2), end(range2)]`, and `[result, result + N]`.
+//
+// Effects: Assigns through every iterator `i` in the range
+// `[result, result + N)` a new corresponding value equal to `E(i)`.
+//
+// Returns: `result + N`
+//
+// Complexity: Exactly `N` applications of `binary_op`, and any projections.
+//
+// Remarks: `result` may be equal to `begin(range1)` or `begin(range2)`.
+//
+// Reference: https://wg21.link/alg.transform#:~:text=ranges::transform(R1
+template <typename Range1,
+          typename Range2,
+          typename OutputIterator,
+          typename BinaryOperation,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<BinaryOperation&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>>
+constexpr auto transform(Range1&& range1,
+                         Range2&& range2,
+                         OutputIterator result,
+                         BinaryOperation binary_op,
+                         Proj1 proj1 = {},
+                         Proj2 proj2 = {}) {
+  return ranges::transform(ranges::begin(range1), ranges::end(range1),
+                           ranges::begin(range2), ranges::end(range2), result,
+                           std::move(binary_op), std::move(proj1),
+                           std::move(proj2));
+}
+
+// [alg.replace] Replace
+// Reference: https://wg21.link/alg.replace
+
+// Let `E(i)` be `bool(invoke(proj, *i) == old_value)`.
+//
+// Mandates: `new_value` is writable  to `first`.
+//
+// Effects: Substitutes elements referred by the iterator `i` in the range
+// `[first, last)` with `new_value`, when `E(i)` is true.
+//
+// Returns: `last`
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace(I
+template <typename ForwardIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto replace(ForwardIterator first,
+                       ForwardIterator last,
+                       const T& old_value,
+                       const T& new_value,
+                       Proj proj = {}) {
+  // Note: In order to be able to apply `proj` to each element in [first, last)
+  // we are dispatching to std::replace_if instead of std::replace.
+  std::replace_if(
+      first, last,
+      [&proj, &old_value](auto&& lhs) {
+        return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) ==
+               old_value;
+      },
+      new_value);
+  return last;
+}
+
+// Let `E(i)` be `bool(invoke(proj, *i) == old_value)`.
+//
+// Mandates: `new_value` is writable  to `begin(range)`.
+//
+// Effects: Substitutes elements referred by the iterator `i` in `range` with
+// `new_value`, when `E(i)` is true.
+//
+// Returns: `end(range)`
+//
+// Complexity: Exactly `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace(R
+template <typename Range,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto replace(Range&& range,
+                       const T& old_value,
+                       const T& new_value,
+                       Proj proj = {}) {
+  return ranges::replace(ranges::begin(range), ranges::end(range), old_value,
+                         new_value, std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Mandates: `new_value` is writable  to `first`.
+//
+// Effects: Substitutes elements referred by the iterator `i` in the range
+// `[first, last)` with `new_value`, when `E(i)` is true.
+//
+// Returns: `last`
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_if(I
+template <typename ForwardIterator,
+          typename Predicate,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto replace_if(ForwardIterator first,
+                          ForwardIterator last,
+                          Predicate pred,
+                          const T& new_value,
+                          Proj proj = {}) {
+  std::replace_if(first, last, internal::ProjectedUnaryPredicate(pred, proj),
+                  new_value);
+  return last;
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Mandates: `new_value` is writable  to `begin(range)`.
+//
+// Effects: Substitutes elements referred by the iterator `i` in `range` with
+// `new_value`, when `E(i)` is true.
+//
+// Returns: `end(range)`
+//
+// Complexity: Exactly `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_if(R
+template <typename Range,
+          typename Predicate,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto replace_if(Range&& range,
+                          Predicate pred,
+                          const T& new_value,
+                          Proj proj = {}) {
+  return ranges::replace_if(ranges::begin(range), ranges::end(range),
+                            std::move(pred), new_value, std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(proj, *(first + (i - result))) == old_value)`.
+//
+// Mandates: The results of the expressions `*first` and `new_value` are
+// writable  to `result`.
+//
+// Preconditions: The ranges `[first, last)` and `[result, result + (last -
+// first))` do not overlap.
+//
+// Effects: Assigns through every iterator `i` in the range `[result, result +
+// (last - first))` a new corresponding value, `new_value` if `E(i)` is true, or
+// `*(first + (i - result))` otherwise.
+//
+// Returns: `result + (last - first)`.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto replace_copy(InputIterator first,
+                            InputIterator last,
+                            OutputIterator result,
+                            const T& old_value,
+                            const T& new_value,
+                            Proj proj = {}) {
+  // Note: In order to be able to apply `proj` to each element in [first, last)
+  // we are dispatching to std::replace_copy_if instead of std::replace_copy.
+  std::replace_copy_if(
+      first, last, result,
+      [&proj, &old_value](auto&& lhs) {
+        return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) ==
+               old_value;
+      },
+      new_value);
+  return last;
+}
+
+// Let `E(i)` be
+// `bool(invoke(proj, *(begin(range) + (i - result))) == old_value)`.
+//
+// Mandates: The results of the expressions `*begin(range)` and `new_value` are
+// writable  to `result`.
+//
+// Preconditions: The ranges `range` and `[result, result + size(range))` do not
+// overlap.
+//
+// Effects: Assigns through every iterator `i` in the range `[result, result +
+// size(range))` a new corresponding value, `new_value` if `E(i)` is true, or
+// `*(begin(range) + (i - result))` otherwise.
+//
+// Returns: `result + size(range)`.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy(R
+template <typename Range,
+          typename OutputIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto replace_copy(Range&& range,
+                            OutputIterator result,
+                            const T& old_value,
+                            const T& new_value,
+                            Proj proj = {}) {
+  return ranges::replace_copy(ranges::begin(range), ranges::end(range), result,
+                              old_value, new_value, std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *(first + (i - result)))))`.
+//
+// Mandates: The results of the expressions `*first` and `new_value` are
+// writable  to `result`.
+//
+// Preconditions: The ranges `[first, last)` and `[result, result + (last -
+// first))` do not overlap.
+//
+// Effects: Assigns through every iterator `i` in the range `[result, result +
+// (last - first))` a new corresponding value, `new_value` if `E(i)` is true, or
+// `*(first + (i - result))` otherwise.
+//
+// Returns: `result + (last - first)`.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy_if(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename Predicate,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto replace_copy_if(InputIterator first,
+                               InputIterator last,
+                               OutputIterator result,
+                               Predicate pred,
+                               const T& new_value,
+                               Proj proj = {}) {
+  return std::replace_copy_if(first, last, result,
+                              internal::ProjectedUnaryPredicate(pred, proj),
+                              new_value);
+}
+
+// Let `E(i)` be
+// `bool(invoke(pred, invoke(proj, *(begin(range) + (i - result)))))`.
+//
+// Mandates: The results of the expressions `*begin(range)` and `new_value` are
+// writable  to `result`.
+//
+// Preconditions: The ranges `range` and `[result, result + size(range))` do not
+// overlap.
+//
+// Effects: Assigns through every iterator `i` in the range `[result, result +
+// size(range))` a new corresponding value, `new_value` if `E(i)` is true, or
+// `*(begin(range) + (i - result))` otherwise.
+//
+// Returns: `result + size(range)`.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.replace#:~:text=ranges::replace_copy_if(R
+template <typename Range,
+          typename OutputIterator,
+          typename Predicate,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto replace_copy_if(Range&& range,
+                               OutputIterator result,
+                               Predicate pred,
+                               const T& new_value,
+                               Proj proj = {}) {
+  return ranges::replace_copy_if(ranges::begin(range), ranges::end(range),
+                                 result, pred, new_value, std::move(proj));
+}
+
+// [alg.fill] Fill
+// Reference: https://wg21.link/alg.fill
+
+// Let `N` be `last - first`.
+//
+// Mandates: The expression `value` is writable to the output iterator.
+//
+// Effects: Assigns `value` through all the iterators in the range
+// `[first, last)`.
+//
+// Returns: `last`.
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill(O
+template <typename OutputIterator,
+          typename T,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto fill(OutputIterator first, OutputIterator last, const T& value) {
+  std::fill(first, last, value);
+  return last;
+}
+
+// Let `N` be `size(range)`.
+//
+// Mandates: The expression `value` is writable to the output iterator.
+//
+// Effects: Assigns `value` through all the iterators in `range`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill(R
+template <typename Range,
+          typename T,
+          typename = internal::range_category_t<Range>>
+constexpr auto fill(Range&& range, const T& value) {
+  return ranges::fill(ranges::begin(range), ranges::end(range), value);
+}
+
+// Let `N` be `max(0, n)`.
+//
+// Mandates: The expression `value` is writable to the output iterator.
+// The type `Size` is convertible to an integral type.
+//
+// Effects: Assigns `value` through all the iterators in `[first, first + N)`.
+//
+// Returns: `first + N`.
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.fill#:~:text=ranges::fill_n(O
+template <typename OutputIterator,
+          typename Size,
+          typename T,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto fill_n(OutputIterator first, Size n, const T& value) {
+  return std::fill_n(first, n, value);
+}
+
+// [alg.generate] Generate
+// Reference: https://wg21.link/alg.generate
+
+// Let `N` be `last - first`.
+//
+// Effects: Assigns the result of successive evaluations of gen() through each
+// iterator in the range `[first, last)`.
+//
+// Returns: `last`.
+//
+// Complexity: Exactly `N` evaluations of `gen()` and assignments.
+//
+// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate(O
+template <typename OutputIterator,
+          typename Generator,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto generate(OutputIterator first,
+                        OutputIterator last,
+                        Generator gen) {
+  std::generate(first, last, std::move(gen));
+  return last;
+}
+
+// Let `N` be `size(range)`.
+//
+// Effects: Assigns the result of successive evaluations of gen() through each
+// iterator in `range`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Exactly `N` evaluations of `gen()` and assignments.
+//
+// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate(R
+template <typename Range,
+          typename Generator,
+          typename = internal::range_category_t<Range>>
+constexpr auto generate(Range&& range, Generator gen) {
+  return ranges::generate(ranges::begin(range), ranges::end(range),
+                          std::move(gen));
+}
+
+// Let `N` be `max(0, n)`.
+//
+// Mandates: `Size` is convertible to an integral type.
+//
+// Effects: Assigns the result of successive evaluations of gen() through each
+// iterator in the range `[first, first + N)`.
+//
+// Returns: `first + N`.
+//
+// Complexity: Exactly `N` evaluations of `gen()` and assignments.
+//
+// Reference: https://wg21.link/alg.generate#:~:text=ranges::generate_n(O
+template <typename OutputIterator,
+          typename Size,
+          typename Generator,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto generate_n(OutputIterator first, Size n, Generator gen) {
+  return std::generate_n(first, n, std::move(gen));
+}
+
+// [alg.remove] Remove
+// Reference: https://wg21.link/alg.remove
+
+// Let `E(i)` be `bool(invoke(proj, *i) == value)`.
+//
+// Effects: Eliminates all the elements referred to by iterator `i` in the range
+// `[first, last)` for which `E(i)` holds.
+//
+// Returns: The end of the resulting range.
+//
+// Remarks: Stable.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove(I
+template <typename ForwardIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto remove(ForwardIterator first,
+                      ForwardIterator last,
+                      const T& value,
+                      Proj proj = {}) {
+  // Note: In order to be able to apply `proj` to each element in [first, last)
+  // we are dispatching to std::remove_if instead of std::remove.
+  return std::remove_if(first, last, [&proj, &value](auto&& lhs) {
+    return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value;
+  });
+}
+
+// Let `E(i)` be `bool(invoke(proj, *i) == value)`.
+//
+// Effects: Eliminates all the elements referred to by iterator `i` in `range`
+// for which `E(i)` holds.
+//
+// Returns: The end of the resulting range.
+//
+// Remarks: Stable.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove(R
+template <typename Range,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto remove(Range&& range, const T& value, Proj proj = {}) {
+  return ranges::remove(ranges::begin(range), ranges::end(range), value,
+                        std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Effects: Eliminates all the elements referred to by iterator `i` in the range
+// `[first, last)` for which `E(i)` holds.
+//
+// Returns: The end of the resulting range.
+//
+// Remarks: Stable.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_if(I
+template <typename ForwardIterator,
+          typename Predicate,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto remove_if(ForwardIterator first,
+                         ForwardIterator last,
+                         Predicate pred,
+                         Proj proj = {}) {
+  return std::remove_if(first, last,
+                        internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Effects: Eliminates all the elements referred to by iterator `i` in `range`.
+//
+// Returns: The end of the resulting range.
+//
+// Remarks: Stable.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding predicate
+// and any projection.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_if(R
+template <typename Range,
+          typename Predicate,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto remove_if(Range&& range, Predicate pred, Proj proj = {}) {
+  return ranges::remove_if(ranges::begin(range), ranges::end(range),
+                           std::move(pred), std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(proj, *i) == value)`.
+//
+// Let `N` be the number of elements in `[first, last)` for which `E(i)` is
+// false.
+//
+// Mandates: `*first` is writable to `result`.
+//
+// Preconditions: The ranges `[first, last)` and `[result, result + (last -
+// first))` do not overlap.
+//
+// Effects: Copies all the elements referred to by the iterator `i` in the range
+// `[first, last)` for which `E(i)` is false.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto remove_copy(InputIterator first,
+                           InputIterator last,
+                           OutputIterator result,
+                           const T& value,
+                           Proj proj = {}) {
+  // Note: In order to be able to apply `proj` to each element in [first, last)
+  // we are dispatching to std::remove_copy_if instead of std::remove_copy.
+  return std::remove_copy_if(first, last, result, [&proj, &value](auto&& lhs) {
+    return gurl_base::invoke(proj, std::forward<decltype(lhs)>(lhs)) == value;
+  });
+}
+
+// Let `E(i)` be `bool(invoke(proj, *i) == value)`.
+//
+// Let `N` be the number of elements in `range` for which `E(i)` is false.
+//
+// Mandates: `*begin(range)` is writable to `result`.
+//
+// Preconditions: The ranges `range` and `[result, result + size(range))` do not
+// overlap.
+//
+// Effects: Copies all the elements referred to by the iterator `i` in `range`
+//  for which `E(i)` is false.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding
+// predicate and any projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(R
+template <typename Range,
+          typename OutputIterator,
+          typename T,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto remove_copy(Range&& range,
+                           OutputIterator result,
+                           const T& value,
+                           Proj proj = {}) {
+  return ranges::remove_copy(ranges::begin(range), ranges::end(range), result,
+                             value, std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Let `N` be the number of elements in `[first, last)` for which `E(i)` is
+// false.
+//
+// Mandates: `*first` is writable to `result`.
+//
+// Preconditions: The ranges `[first, last)` and `[result, result + (last -
+// first))` do not overlap.
+//
+// Effects: Copies all the elements referred to by the iterator `i` in the range
+// `[first, last)` for which `E(i)` is false.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `last - first` applications of the corresponding
+// predicate and any projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy_if(I
+template <typename InputIterator,
+          typename OutputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto remove_copy_if(InputIterator first,
+                              InputIterator last,
+                              OutputIterator result,
+                              Pred pred,
+                              Proj proj = {}) {
+  return std::remove_copy_if(first, last, result,
+                             internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(i)` be `bool(invoke(pred, invoke(proj, *i)))`.
+//
+// Let `N` be the number of elements in `range` for which `E(i)` is false.
+//
+// Mandates: `*begin(range)` is writable to `result`.
+//
+// Preconditions: The ranges `range` and `[result, result + size(range))` do not
+// overlap.
+//
+// Effects: Copies all the elements referred to by the iterator `i` in `range`
+//  for which `E(i)` is false.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `size(range)` applications of the corresponding
+// predicate and any projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.remove#:~:text=ranges::remove_copy(R
+template <typename Range,
+          typename OutputIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto remove_copy_if(Range&& range,
+                              OutputIterator result,
+                              Pred pred,
+                              Proj proj = {}) {
+  return ranges::remove_copy_if(ranges::begin(range), ranges::end(range),
+                                result, std::move(pred), std::move(proj));
+}
+
+// [alg.unique] Unique
+// Reference: https://wg21.link/alg.unique
+
+// Let `E(i)` be `bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i)))`.
+//
+// Effects: For a nonempty range, eliminates all but the first element from
+// every consecutive group of equivalent elements referred to by the iterator
+// `i` in the range `[first + 1, last)` for which `E(i)` is true.
+//
+// Returns: The end of the resulting range.
+//
+// Complexity: For nonempty ranges, exactly `(last - first) - 1` applications of
+// the corresponding predicate and no more than twice as many applications of
+// any projection.
+//
+// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique(I
+template <typename ForwardIterator,
+          typename Comp = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator, Proj>,
+                                       projected<ForwardIterator, Proj>>>
+constexpr auto unique(ForwardIterator first,
+                      ForwardIterator last,
+                      Comp comp = {},
+                      Proj proj = {}) {
+  return std::unique(first, last,
+                     internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Let `E(i)` be `bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i)))`.
+//
+// Effects: For a nonempty range, eliminates all but the first element from
+// every consecutive group of equivalent elements referred to by the iterator
+// `i` in the range `[begin(range) + 1, end(range))` for which `E(i)` is true.
+//
+// Returns: The end of the resulting range.
+//
+// Complexity: For nonempty ranges, exactly `size(range) - 1` applications of
+// the corresponding predicate and no more than twice as many applications of
+// any projection.
+//
+// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique(R
+template <typename Range,
+          typename Comp = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto unique(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::unique(ranges::begin(range), ranges::end(range),
+                        std::move(comp), std::move(proj));
+}
+
+// Let `E(i)` be `bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1))))`.
+//
+// Mandates: `*first` is writable to `result`.
+//
+// Preconditions: The ranges `[first, last)` and
+// `[result, result + (last - first))` do not overlap.
+//
+// Effects: Copies only the first element from every consecutive group of equal
+// elements referred to by the iterator `i` in the range `[first, last)` for
+// which `E(i)` holds.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `last - first - 1` applications of the corresponding
+// predicate and no more than twice as many applications of any projection.
+//
+// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique_copy(I
+template <typename ForwardIterator,
+          typename OutputIterator,
+          typename Comp = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto unique_copy(ForwardIterator first,
+                           ForwardIterator last,
+                           OutputIterator result,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return std::unique_copy(first, last, result,
+                          internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Let `E(i)` be `bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1))))`.
+//
+// Mandates: `*begin(range)` is writable to `result`.
+//
+// Preconditions: The ranges `range` and `[result, result + size(range))` do not
+// overlap.
+//
+// Effects: Copies only the first element from every consecutive group of equal
+// elements referred to by the iterator `i` in `range` for which `E(i)` holds.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `size(range) - 1` applications of the corresponding
+// predicate and no more than twice as many applications of any projection.
+//
+// Reference: https://wg21.link/alg.unique#:~:text=ranges::unique_copy(R
+template <typename Range,
+          typename OutputIterator,
+          typename Comp = ranges::equal_to,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto unique_copy(Range&& range,
+                           OutputIterator result,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return ranges::unique_copy(ranges::begin(range), ranges::end(range), result,
+                             std::move(comp), std::move(proj));
+}
+
+// [alg.reverse] Reverse
+// Reference: https://wg21.link/alg.reverse
+
+// Effects: For each non-negative integer `i < (last - first) / 2`, applies
+// `std::iter_swap` to all pairs of iterators `first + i, (last - i) - 1`.
+//
+// Returns: `last`.
+//
+// Complexity: Exactly `(last - first)/2` swaps.
+//
+// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse(I
+template <typename BidirectionalIterator,
+          typename = internal::iterator_category_t<BidirectionalIterator>>
+constexpr auto reverse(BidirectionalIterator first,
+                       BidirectionalIterator last) {
+  std::reverse(first, last);
+  return last;
+}
+
+// Effects: For each non-negative integer `i < size(range) / 2`, applies
+// `std::iter_swap` to all pairs of iterators
+// `begin(range) + i, (end(range) - i) - 1`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Exactly `size(range)/2` swaps.
+//
+// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse(R
+template <typename Range, typename = internal::range_category_t<Range>>
+constexpr auto reverse(Range&& range) {
+  return ranges::reverse(ranges::begin(range), ranges::end(range));
+}
+
+// Let `N` be `last - first`.
+//
+// Preconditions: The ranges `[first, last)` and `[result, result + N)` do not
+// overlap.
+//
+// Effects: Copies the range `[first, last)` to the range `[result, result + N)`
+// such that for every non-negative integer `i < N` the following assignment
+// takes place: `*(result + N - 1 - i) = *(first + i)`.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse_copy(I
+template <typename BidirectionalIterator,
+          typename OutputIterator,
+          typename = internal::iterator_category_t<BidirectionalIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto reverse_copy(BidirectionalIterator first,
+                            BidirectionalIterator last,
+                            OutputIterator result) {
+  return std::reverse_copy(first, last, result);
+}
+
+// Let `N` be `size(range)`.
+//
+// Preconditions: The ranges `range` and `[result, result + N)` do not
+// overlap.
+//
+// Effects: Copies `range` to the range `[result, result + N)` such that for
+// every non-negative integer `i < N` the following assignment takes place:
+// `*(result + N - 1 - i) = *(begin(range) + i)`.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.reverse#:~:text=ranges::reverse_copy(R
+template <typename Range,
+          typename OutputIterator,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto reverse_copy(Range&& range, OutputIterator result) {
+  return ranges::reverse_copy(ranges::begin(range), ranges::end(range), result);
+}
+
+// [alg.rotate] Rotate
+// Reference: https://wg21.link/alg.rotate
+
+// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges.
+//
+// Effects: For each non-negative integer `i < (last - first)`, places the
+// element from the position `first + i` into position
+// `first + (i + (last - middle)) % (last - first)`.
+//
+// Returns: `first + (last - middle)`.
+//
+// Complexity: At most `last - first` swaps.
+//
+// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate(I
+template <typename ForwardIterator,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto rotate(ForwardIterator first,
+                      ForwardIterator middle,
+                      ForwardIterator last) {
+  return std::rotate(first, middle, last);
+}
+
+// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid
+// ranges.
+//
+// Effects: For each non-negative integer `i < size(range)`, places the element
+// from the position `begin(range) + i` into position
+// `begin(range) + (i + (end(range) - middle)) % size(range)`.
+//
+// Returns: `begin(range) + (end(range) - middle)`.
+//
+// Complexity: At most `size(range)` swaps.
+//
+// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate(R
+template <typename Range, typename = internal::range_category_t<Range>>
+constexpr auto rotate(Range&& range, iterator_t<Range> middle) {
+  return ranges::rotate(ranges::begin(range), middle, ranges::end(range));
+}
+
+// Let `N` be `last - first`.
+//
+// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges. The
+// ranges `[first, last)` and `[result, result + N)` do not overlap.
+//
+// Effects: Copies the range `[first, last)` to the range `[result, result + N)`
+// such that for each non-negative integer `i < N` the following assignment
+// takes place: `*(result + i) = *(first + (i + (middle - first)) % N)`.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate_copy(I
+template <typename ForwardIterator,
+          typename OutputIterator,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto rotate_copy(ForwardIterator first,
+                           ForwardIterator middle,
+                           ForwardIterator last,
+                           OutputIterator result) {
+  return std::rotate_copy(first, middle, last, result);
+}
+
+// Let `N` be `size(range)`.
+//
+// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid
+// ranges. The ranges `range` and `[result, result + N)` do not overlap.
+//
+// Effects: Copies `range` to the range `[result, result + N)` such that for
+// each non-negative integer `i < N` the following assignment takes place:
+// `*(result + i) = *(begin(range) + (i + (middle - begin(range))) % N)`.
+//
+// Returns: `result + N`.
+//
+// Complexity: Exactly `N` assignments.
+//
+// Reference: https://wg21.link/alg.rotate#:~:text=ranges::rotate_copy(R
+template <typename Range,
+          typename OutputIterator,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator>>
+constexpr auto rotate_copy(Range&& range,
+                           iterator_t<Range> middle,
+                           OutputIterator result) {
+  return ranges::rotate_copy(ranges::begin(range), middle, ranges::end(range),
+                             result);
+}
+
+// [alg.random.sample] Sample
+// Reference: https://wg21.link/alg.random.sample
+
+// Currently not implemented due to lack of std::sample in C++14.
+// TODO(crbug.com/1071094): Consider implementing a hand-rolled version.
+
+// [alg.random.shuffle] Shuffle
+// Reference: https://wg21.link/alg.random.shuffle
+
+// Preconditions: The type `std::remove_reference_t<UniformRandomBitGenerator>`
+// meets the uniform random bit generator requirements.
+//
+// Effects: Permutes the elements in the range `[first, last)` such that each
+// possible permutation of those elements has equal probability of appearance.
+//
+// Returns: `last`.
+//
+// Complexity: Exactly `(last - first) - 1` swaps.
+//
+// Remarks: To the extent that the implementation of this function makes use of
+// random numbers, the object referenced by g shall serve as the
+// implementation's source of randomness.
+//
+// Reference: https://wg21.link/alg.random.shuffle#:~:text=ranges::shuffle(I
+template <typename RandomAccessIterator,
+          typename UniformRandomBitGenerator,
+          typename = internal::iterator_category_t<RandomAccessIterator>>
+constexpr auto shuffle(RandomAccessIterator first,
+                       RandomAccessIterator last,
+                       UniformRandomBitGenerator&& g) {
+  std::shuffle(first, last, std::forward<UniformRandomBitGenerator>(g));
+  return last;
+}
+
+// Preconditions: The type `std::remove_reference_t<UniformRandomBitGenerator>`
+// meets the uniform random bit generator requirements.
+//
+// Effects: Permutes the elements in `range` such that each possible permutation
+// of those elements has equal probability of appearance.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Exactly `size(range) - 1` swaps.
+//
+// Remarks: To the extent that the implementation of this function makes use of
+// random numbers, the object referenced by g shall serve as the
+// implementation's source of randomness.
+//
+// Reference: https://wg21.link/alg.random.shuffle#:~:text=ranges::shuffle(R
+template <typename Range,
+          typename UniformRandomBitGenerator,
+          typename = internal::range_category_t<Range>>
+constexpr auto shuffle(Range&& range, UniformRandomBitGenerator&& g) {
+  return ranges::shuffle(ranges::begin(range), ranges::end(range),
+                         std::forward<UniformRandomBitGenerator>(g));
+}
+
+// [alg.nonmodifying] Sorting and related operations
+// Reference: https://wg21.link/alg.sorting
+
+// [alg.sort] Sorting
+// Reference: https://wg21.link/alg.sort
+
+// [sort] sort
+// Reference: https://wg21.link/sort
+
+// Effects: Sorts the elements in the range `[first, last)` with respect to
+// `comp` and `proj`.
+//
+// Returns: `last`.
+//
+// Complexity: Let `N` be `last - first`. `O(N log N)` comparisons and
+// projections.
+//
+// Reference: https://wg21.link/sort#:~:text=ranges::sort(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto sort(RandomAccessIterator first,
+                    RandomAccessIterator last,
+                    Comp comp = {},
+                    Proj proj = {}) {
+  std::sort(first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Effects: Sorts the elements in `range` with respect to `comp` and `proj`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Let `N` be `size(range)`. `O(N log N)` comparisons and
+// projections.
+//
+// Reference: https://wg21.link/sort#:~:text=ranges::sort(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto sort(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::sort(ranges::begin(range), ranges::end(range), std::move(comp),
+                      std::move(proj));
+}
+
+// [stable.sort] stable_sort
+// Reference: https://wg21.link/stable.sort
+
+// Effects: Sorts the elements in the range `[first, last)` with respect to
+// `comp` and `proj`.
+//
+// Returns: `last`.
+//
+// Complexity: Let `N` be `last - first`. If enough extra memory is available,
+// `N log (N)` comparisons. Otherwise, at most `N log^2 (N)` comparisons. In
+// either case, twice as many projections as the number of comparisons.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/stable.sort#:~:text=ranges::stable_sort(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto stable_sort(RandomAccessIterator first,
+                           RandomAccessIterator last,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  std::stable_sort(first, last,
+                   internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Effects: Sorts the elements in `range` with respect to `comp` and `proj`.
+//
+// Returns: `end(rang)`.
+//
+// Complexity: Let `N` be `size(range)`. If enough extra memory is available,
+// `N log (N)` comparisons. Otherwise, at most `N log^2 (N)` comparisons. In
+// either case, twice as many projections as the number of comparisons.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/stable.sort#:~:text=ranges::stable_sort(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto stable_sort(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::stable_sort(ranges::begin(range), ranges::end(range),
+                             std::move(comp), std::move(proj));
+}
+
+// [partial.sort] partial_sort
+// Reference: https://wg21.link/partial.sort
+
+// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges.
+//
+// Effects: Places the first `middle - first` elements from the range
+// `[first, last)` as sorted with respect to `comp` and `proj` into the range
+// `[first, middle)`. The rest of the elements in the range `[middle, last)` are
+// placed in an unspecified order.
+//
+// Returns: `last`.
+//
+// Complexity: Approximately `(last - first) * log(middle - first)` comparisons,
+// and twice as many projections.
+//
+// Reference: https://wg21.link/partial.sort#:~:text=ranges::partial_sort(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto partial_sort(RandomAccessIterator first,
+                            RandomAccessIterator middle,
+                            RandomAccessIterator last,
+                            Comp comp = {},
+                            Proj proj = {}) {
+  std::partial_sort(first, middle, last,
+                    internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid
+// ranges.
+//
+// Effects: Places the first `middle - begin(range)` elements from `range` as
+// sorted with respect to `comp` and `proj` into the range
+// `[begin(range), middle)`. The rest of the elements in the range
+// `[middle, end(range))` are placed in an unspecified order.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Approximately `size(range) * log(middle - begin(range))`
+// comparisons, and twice as many projections.
+//
+// Reference: https://wg21.link/partial.sort#:~:text=ranges::partial_sort(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto partial_sort(Range&& range,
+                            iterator_t<Range> middle,
+                            Comp comp = {},
+                            Proj proj = {}) {
+  return ranges::partial_sort(ranges::begin(range), middle, ranges::end(range),
+                              std::move(comp), std::move(proj));
+}
+
+// [partial.sort.copy] partial_sort_copy
+// Reference: https://wg21.link/partial.sort.copy
+
+// Let `N` be `min(last - first, result_last - result_first)`.
+//
+// Preconditions: For iterators `a1` and `b1` in `[first, last)`, and iterators
+// `x2` and `y2` in `[result_first, result_last)`, after evaluating the
+// assignment `*y2 = *b1`, let `E` be the value of `bool(invoke(comp,
+// invoke(proj1, *a1), invoke(proj2, *y2)))`. Then, after evaluating the
+// assignment `*x2 = *a1`, `E` is equal to `bool(invoke(comp, invoke(proj2,
+// *x2), invoke(proj2, *y2)))`.
+//
+// Effects: Places the first `N` elements as sorted with respect to `comp` and
+// `proj2` into the range `[result_first, result_first + N)`.
+//
+// Returns: `result_first + N`.
+//
+// Complexity: Approximately `(last - first) * log N` comparisons, and twice as
+// many projections.
+//
+// Reference:
+// https://wg21.link/partial.sort.copy#:~:text=ranges::partial_sort_copy(I1
+template <typename InputIterator,
+          typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator, Proj1>,
+                                       projected<RandomAccessIterator, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj2>,
+                                       projected<InputIterator, Proj1>>>
+constexpr auto partial_sort_copy(InputIterator first,
+                                 InputIterator last,
+                                 RandomAccessIterator result_first,
+                                 RandomAccessIterator result_last,
+                                 Comp comp = {},
+                                 Proj1 proj1 = {},
+                                 Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::partial_sort_copy expects
+  // comp(proj2(lhs), proj1(rhs)) to compile.
+  return std::partial_sort_copy(
+      first, last, result_first, result_last,
+      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
+}
+
+// Let `N` be `min(size(range), size(result_range))`.
+//
+// Preconditions: For iterators `a1` and `b1` in `range`, and iterators
+// `x2` and `y2` in `result_range`, after evaluating the assignment
+// `*y2 = *b1`, let `E` be the value of
+// `bool(invoke(comp, invoke(proj1, *a1), invoke(proj2, *y2)))`. Then, after
+// evaluating the assignment `*x2 = *a1`, `E` is equal to
+// `bool(invoke(comp, invoke(proj2, *x2), invoke(proj2, *y2)))`.
+//
+// Effects: Places the first `N` elements as sorted with respect to `comp` and
+// `proj2` into the range `[begin(result_range), begin(result_range) + N)`.
+//
+// Returns: `begin(result_range) + N`.
+//
+// Complexity: Approximately `size(range) * log N` comparisons, and twice as
+// many projections.
+//
+// Reference:
+// https://wg21.link/partial.sort.copy#:~:text=ranges::partial_sort_copy(R1
+template <typename Range1,
+          typename Range2,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr auto partial_sort_copy(Range1&& range,
+                                 Range2&& result_range,
+                                 Comp comp = {},
+                                 Proj1 proj1 = {},
+                                 Proj2 proj2 = {}) {
+  return ranges::partial_sort_copy(ranges::begin(range), ranges::end(range),
+                                   ranges::begin(result_range),
+                                   ranges::end(result_range), std::move(comp),
+                                   std::move(proj1), std::move(proj2));
+}
+
+// [is.sorted] is_sorted
+// Reference: https://wg21.link/is.sorted
+
+// Returns: The last iterator `i` in `[first, last]` for which the range
+// `[first, i)` is sorted with respect to `comp` and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(I
+template <typename ForwardIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator, Proj>,
+                                       projected<ForwardIterator, Proj>>>
+constexpr auto is_sorted_until(ForwardIterator first,
+                               ForwardIterator last,
+                               Comp comp = {},
+                               Proj proj = {}) {
+  // Implementation inspired by cppreference.com:
+  // https://en.cppreference.com/w/cpp/algorithm/is_sorted_until
+  //
+  // A reimplementation is required, because std::is_sorted_until is not
+  // constexpr prior to C++20. Once we have C++20, we should switch to standard
+  // library implementation.
+  if (first == last)
+    return last;
+
+  for (ForwardIterator next = first; ++next != last; ++first) {
+    if (gurl_base::invoke(comp, gurl_base::invoke(proj, *next),
+                     gurl_base::invoke(proj, *first))) {
+      return next;
+    }
+  }
+
+  return last;
+}
+
+// Returns: The last iterator `i` in `[begin(range), end(range)]` for which the
+// range `[begin(range), i)` is sorted with respect to `comp` and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted_until(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto is_sorted_until(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::is_sorted_until(ranges::begin(range), ranges::end(range),
+                                 std::move(comp), std::move(proj));
+}
+
+// Returns: Whether the range `[first, last)` is sorted with respect to `comp`
+// and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(I
+template <typename ForwardIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator, Proj>,
+                                       projected<ForwardIterator, Proj>>>
+constexpr auto is_sorted(ForwardIterator first,
+                         ForwardIterator last,
+                         Comp comp = {},
+                         Proj proj = {}) {
+  return ranges::is_sorted_until(first, last, std::move(comp),
+                                 std::move(proj)) == last;
+}
+
+// Returns: Whether `range` is sorted with respect to `comp` and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.sorted#:~:text=ranges::is_sorted(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto is_sorted(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::is_sorted(ranges::begin(range), ranges::end(range),
+                           std::move(comp), std::move(proj));
+}
+
+// [alg.nth.element] Nth element
+// Reference: https://wg21.link/alg.nth.element
+
+// Preconditions: `[first, nth)` and `[nth, last)` are valid ranges.
+//
+// Effects: After `nth_element` the element in the position pointed to by `nth`
+// is the element that would be in that position if the whole range were sorted
+// with respect to `comp` and `proj`, unless `nth == last`. Also for every
+// iterator `i` in the range `[first, nth)` and every iterator `j` in the range
+// `[nth, last)` it holds that:
+// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is false.
+//
+// Returns: `last`.
+//
+// Complexity: Linear on average.
+//
+// Reference: https://wg21.link/alg.nth.element#:~:text=ranges::nth_element(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto nth_element(RandomAccessIterator first,
+                           RandomAccessIterator nth,
+                           RandomAccessIterator last,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  std::nth_element(first, nth, last,
+                   internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Preconditions: `[begin(range), nth)` and `[nth, end(range))` are valid
+// ranges.
+//
+// Effects: After `nth_element` the element in the position pointed to by `nth`
+// is the element that would be in that position if the whole range were sorted
+// with respect to `comp` and `proj`, unless `nth == end(range)`. Also for every
+// iterator `i` in the range `[begin(range), nth)` and every iterator `j` in the
+// range `[nth, end(range))` it holds that:
+// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is false.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Linear on average.
+//
+// Reference: https://wg21.link/alg.nth.element#:~:text=ranges::nth_element(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto nth_element(Range&& range,
+                           iterator_t<Range> nth,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return ranges::nth_element(ranges::begin(range), nth, ranges::end(range),
+                             std::move(comp), std::move(proj));
+}
+
+// [alg.binary.search] Binary search
+// Reference: https://wg21.link/alg.binary.search
+
+// [lower.bound] lower_bound
+// Reference: https://wg21.link/lower.bound
+
+// Preconditions: The elements `e` of `[first, last)` are partitioned with
+// respect to the expression `bool(invoke(comp, invoke(proj, e), value))`.
+//
+// Returns: The furthermost iterator `i` in the range `[first, last]` such that
+// for every iterator `j` in the range `[first, i)`,
+// `bool(invoke(comp, invoke(proj, *j), value))` is true.
+//
+// Complexity: At most `log_2(last - first) + O(1)` comparisons and projections.
+//
+// Reference: https://wg21.link/lower.bound#:~:text=ranges::lower_bound(I
+template <typename ForwardIterator,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto lower_bound(ForwardIterator first,
+                           ForwardIterator last,
+                           const T& value,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  // The second arg is guaranteed to be `value`, so we'll simply apply the
+  // identity projection.
+  identity value_proj;
+  return std::lower_bound(
+      first, last, value,
+      internal::ProjectedBinaryPredicate(comp, proj, value_proj));
+}
+
+// Preconditions: The elements `e` of `range` are partitioned with respect to
+// the expression `bool(invoke(comp, invoke(proj, e), value))`.
+//
+// Returns: The furthermost iterator `i` in the range
+// `[begin(range), end(range)]` such that for every iterator `j` in the range
+// `[begin(range), i)`, `bool(invoke(comp, invoke(proj, *j), value))` is true.
+//
+// Complexity: At most `log_2(size(range)) + O(1)` comparisons and projections.
+//
+// Reference: https://wg21.link/lower.bound#:~:text=ranges::lower_bound(R
+template <typename Range,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto lower_bound(Range&& range,
+                           const T& value,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return ranges::lower_bound(ranges::begin(range), ranges::end(range), value,
+                             std::move(comp), std::move(proj));
+}
+
+// [upper.bound] upper_bound
+// Reference: https://wg21.link/upper.bound
+
+// Preconditions: The elements `e` of `[first, last)` are partitioned with
+// respect to the expression `!bool(invoke(comp, value, invoke(proj, e)))`.
+//
+// Returns: The furthermost iterator `i` in the range `[first, last]` such that
+// for every iterator `j` in the range `[first, i)`,
+// `!bool(invoke(comp, value, invoke(proj, *j)))` is true.
+//
+// Complexity: At most `log_2(last - first) + O(1)` comparisons and projections.
+//
+// Reference: https://wg21.link/upper.bound#:~:text=ranges::upper_bound(I
+template <typename ForwardIterator,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto upper_bound(ForwardIterator first,
+                           ForwardIterator last,
+                           const T& value,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  // The first arg is guaranteed to be `value`, so we'll simply apply the
+  // identity projection.
+  identity value_proj;
+  return std::upper_bound(
+      first, last, value,
+      internal::ProjectedBinaryPredicate(comp, value_proj, proj));
+}
+
+// Preconditions: The elements `e` of `range` are partitioned with
+// respect to the expression `!bool(invoke(comp, value, invoke(proj, e)))`.
+//
+// Returns: The furthermost iterator `i` in the range
+// `[begin(range), end(range)]` such that for every iterator `j` in the range
+// `[begin(range), i)`, `!bool(invoke(comp, value, invoke(proj, *j)))` is true.
+//
+// Complexity: At most `log_2(size(range)) + O(1)` comparisons and projections.
+//
+// Reference: https://wg21.link/upper.bound#:~:text=ranges::upper_bound(R
+template <typename Range,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto upper_bound(Range&& range,
+                           const T& value,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return ranges::upper_bound(ranges::begin(range), ranges::end(range), value,
+                             std::move(comp), std::move(proj));
+}
+
+// [equal.range] equal_range
+// Reference: https://wg21.link/equal.range
+
+// Preconditions: The elements `e` of `[first, last)` are partitioned with
+// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and
+// `!bool(invoke(comp, value, invoke(proj, e)))`.
+//
+// Returns: `{ranges::lower_bound(first, last, value, comp, proj),
+//            ranges::upper_bound(first, last, value, comp, proj)}`.
+//
+// Complexity: At most 2 ∗ log_2(last - first) + O(1) comparisons and
+// projections.
+//
+// Reference: https://wg21.link/equal.range#:~:text=ranges::equal_range(I
+template <typename ForwardIterator,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto equal_range(ForwardIterator first,
+                           ForwardIterator last,
+                           const T& value,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  // Note: This does not dispatch to std::equal_range, as otherwise it would not
+  // be possible to prevent applying `proj` to `value`, which can result in
+  // unintended behavior.
+  return std::make_pair(ranges::lower_bound(first, last, value, comp, proj),
+                        ranges::upper_bound(first, last, value, comp, proj));
+}
+
+// Preconditions: The elements `e` of `range` are partitioned with
+// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and
+// `!bool(invoke(comp, value, invoke(proj, e)))`.
+//
+// Returns: `{ranges::lower_bound(range, value, comp, proj),
+//            ranges::upper_bound(range, value, comp, proj)}`.
+//
+// Complexity: At most 2 ∗ log_2(size(range)) + O(1) comparisons and
+// projections.
+//
+// Reference: https://wg21.link/equal.range#:~:text=ranges::equal_range(R
+template <typename Range,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto equal_range(Range&& range,
+                           const T& value,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return ranges::equal_range(ranges::begin(range), ranges::end(range), value,
+                             std::move(comp), std::move(proj));
+}
+
+// [binary.search] binary_search
+// Reference: https://wg21.link/binary.search
+
+// Preconditions: The elements `e` of `[first, last)` are partitioned with
+// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and
+// `!bool(invoke(comp, value, invoke(proj, e)))`.
+//
+// Returns: `true` if and only if for some iterator `i` in the range
+// `[first, last)`, `!bool(invoke(comp, invoke(proj, *i), value)) &&
+//                   !bool(invoke(comp, value, invoke(proj, *i)))` is true.
+//
+// Complexity: At most `log_2(last - first) + O(1)` comparisons and projections.
+//
+// Reference: https://wg21.link/binary.search#:~:text=ranges::binary_search(I
+template <typename ForwardIterator,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto binary_search(ForwardIterator first,
+                             ForwardIterator last,
+                             const T& value,
+                             Comp comp = {},
+                             Proj proj = {}) {
+  first = ranges::lower_bound(first, last, value, comp, proj);
+  return first != last &&
+         !gurl_base::invoke(comp, value, gurl_base::invoke(proj, *first));
+}
+
+// Preconditions: The elements `e` of `range` are partitioned with
+// respect to the expressions `bool(invoke(comp, invoke(proj, e), value))` and
+// `!bool(invoke(comp, value, invoke(proj, e)))`.
+//
+// Returns: `true` if and only if for some iterator `i` in `range`
+// `!bool(invoke(comp, invoke(proj, *i), value)) &&
+//  !bool(invoke(comp, value, invoke(proj, *i)))` is true.
+//
+// Complexity: At most `log_2(size(range)) + O(1)` comparisons and projections.
+//
+// Reference: https://wg21.link/binary.search#:~:text=ranges::binary_search(R
+template <typename Range,
+          typename T,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto binary_search(Range&& range,
+                             const T& value,
+                             Comp comp = {},
+                             Proj proj = {}) {
+  return ranges::binary_search(ranges::begin(range), ranges::end(range), value,
+                               std::move(comp), std::move(proj));
+}
+
+// [alg.partitions] Partitions
+// Reference: https://wg21.link/alg.partitions
+
+// Returns: `true` if and only if the elements `e` of `[first, last)` are
+// partitioned with respect to the expression
+// `bool(invoke(pred, invoke(proj, e)))`.
+//
+// Complexity: Linear. At most `last - first` applications of `pred` and `proj`.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::is_partitioned(I
+template <typename ForwardIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto is_partitioned(ForwardIterator first,
+                              ForwardIterator last,
+                              Pred pred,
+                              Proj proj = {}) {
+  return std::is_partitioned(first, last,
+                             internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Returns: `true` if and only if the elements `e` of `range` are partitioned
+// with respect to the expression `bool(invoke(pred, invoke(proj, e)))`.
+//
+// Complexity: Linear. At most `size(range)` applications of `pred` and `proj`.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::is_partitioned(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto is_partitioned(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::is_partitioned(ranges::begin(range), ranges::end(range),
+                                std::move(pred), std::move(proj));
+}
+
+// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Effects: Places all the elements `e` in `[first, last)` that satisfy `E(e)`
+// before all the elements that do not.
+//
+// Returns: Let `i` be an iterator such that `E(*j)` is `true` for every
+// iterator `j` in `[first, i)` and `false` for every iterator `j` in
+// `[i, last)`. Returns: i.
+//
+// Complexity: Let `N = last - first`:
+// Exactly `N` applications of the predicate and projection. At most `N / 2`
+// swaps if the type of `first` models `bidirectional_iterator`, and at most `N`
+// swaps otherwise.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition(I
+template <typename ForwardIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto partition(ForwardIterator first,
+                         ForwardIterator last,
+                         Pred pred,
+                         Proj proj = {}) {
+  return std::partition(first, last,
+                        internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Effects: Places all the elements `e` in `range` that satisfy `E(e)` before
+// all the elements that do not.
+//
+// Returns: Let `i` be an iterator such that `E(*j)` is `true` for every
+// iterator `j` in `[begin(range), i)` and `false` for every iterator `j` in
+// `[i, last)`. Returns: i.
+//
+// Complexity: Let `N = size(range)`:
+// Exactly `N` applications of the predicate and projection. At most `N / 2`
+// swaps if the type of `first` models `bidirectional_iterator`, and at most `N`
+// swaps otherwise.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto partition(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::partition(ranges::begin(range), ranges::end(range),
+                           std::move(pred), std::move(proj));
+}
+
+// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Effects: Places all the elements `e` in `[first, last)` that satisfy `E(e)`
+// before all the elements that do not. The relative order of the elements in
+// both groups is preserved.
+//
+// Returns: Let `i` be an iterator such that for every iterator `j` in
+// `[first, i)`, `E(*j)` is `true`, and for every iterator `j` in the range
+// `[i, last)`, `E(*j)` is `false`. Returns: `i`.
+//
+// Complexity: Let `N = last - first`:
+// At most `N log N` swaps, but only `O(N)` swaps if there is enough extra
+// memory. Exactly `N` applications of the predicate and projection.
+//
+// Reference:
+// https://wg21.link/alg.partitions#:~:text=ranges::stable_partition(I
+template <typename BidirectionalIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<BidirectionalIterator>>
+constexpr auto stable_partition(BidirectionalIterator first,
+                                BidirectionalIterator last,
+                                Pred pred,
+                                Proj proj = {}) {
+  return std::stable_partition(first, last,
+                               internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Effects: Places all the elements `e` in `range` that satisfy `E(e)` before
+// all the elements that do not. The relative order of the elements in both
+// groups is preserved.
+//
+// Returns: Let `i` be an iterator such that for every iterator `j` in
+// `[begin(range), i)`, `E(*j)` is `true`, and for every iterator `j` in the
+// range `[i, end(range))`, `E(*j)` is `false`. Returns: `i`.
+//
+// Complexity: Let `N = size(range)`:
+// At most `N log N` swaps, but only `O(N)` swaps if there is enough extra
+// memory. Exactly `N` applications of the predicate and projection.
+//
+// Reference:
+// https://wg21.link/alg.partitions#:~:text=ranges::stable_partition(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto stable_partition(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::stable_partition(ranges::begin(range), ranges::end(range),
+                                  std::move(pred), std::move(proj));
+}
+
+// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Mandates: The expression `*first` is writable to `out_true` and `out_false`.
+//
+// Preconditions: The input range and output ranges do not overlap.
+//
+// Effects: For each iterator `i` in `[first, last)`, copies `*i` to the output
+// range beginning with `out_true` if `E(*i)` is `true`, or to the output range
+// beginning with `out_false` otherwise.
+//
+// Returns: Let `o1` be the end of the output range beginning at `out_true`, and
+// `o2` the end of the output range beginning at `out_false`.
+// Returns `{o1, o2}`.
+//
+// Complexity: Exactly `last - first` applications of `pred` and `proj`.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_copy(I
+template <typename InputIterator,
+          typename OutputIterator1,
+          typename OutputIterator2,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<InputIterator>,
+          typename = internal::iterator_category_t<OutputIterator1>,
+          typename = internal::iterator_category_t<OutputIterator2>>
+constexpr auto partition_copy(InputIterator first,
+                              InputIterator last,
+                              OutputIterator1 out_true,
+                              OutputIterator2 out_false,
+                              Pred pred,
+                              Proj proj = {}) {
+  return std::partition_copy(first, last, out_true, out_false,
+                             internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// Let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Mandates: The expression `*begin(range)` is writable to `out_true` and
+// `out_false`.
+//
+// Preconditions: The input range and output ranges do not overlap.
+//
+// Effects: For each iterator `i` in `range`, copies `*i` to the output range
+// beginning with `out_true` if `E(*i)` is `true`, or to the output range
+// beginning with `out_false` otherwise.
+//
+// Returns: Let `o1` be the end of the output range beginning at `out_true`, and
+// `o2` the end of the output range beginning at `out_false`.
+// Returns `{o1, o2}`.
+//
+// Complexity: Exactly `size(range)` applications of `pred` and `proj`.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_copy(R
+template <typename Range,
+          typename OutputIterator1,
+          typename OutputIterator2,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = internal::iterator_category_t<OutputIterator1>,
+          typename = internal::iterator_category_t<OutputIterator2>>
+constexpr auto partition_copy(Range&& range,
+                              OutputIterator1 out_true,
+                              OutputIterator2 out_false,
+                              Pred pred,
+                              Proj proj = {}) {
+  return ranges::partition_copy(ranges::begin(range), ranges::end(range),
+                                out_true, out_false, std::move(pred),
+                                std::move(proj));
+}
+
+// let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Preconditions: The elements `e` of `[first, last)` are partitioned with
+// respect to `E(e)`.
+//
+// Returns: An iterator `mid` such that `E(*i)` is `true` for all iterators `i`
+// in `[first, mid)`, and `false` for all iterators `i` in `[mid, last)`.
+//
+// Complexity: `O(log(last - first))` applications of `pred` and `proj`.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_point(I
+template <typename ForwardIterator,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>>
+constexpr auto partition_point(ForwardIterator first,
+                               ForwardIterator last,
+                               Pred pred,
+                               Proj proj = {}) {
+  return std::partition_point(first, last,
+                              internal::ProjectedUnaryPredicate(pred, proj));
+}
+
+// let `E(x)` be `bool(invoke(pred, invoke(proj, x)))`.
+//
+// Preconditions: The elements `e` of `range` are partitioned with respect to
+// `E(e)`.
+//
+// Returns: An iterator `mid` such that `E(*i)` is `true` for all iterators `i`
+// in `[begin(range), mid)`, and `false` for all iterators `i` in
+// `[mid, end(range))`.
+//
+// Complexity: `O(log(size(range)))` applications of `pred` and `proj`.
+//
+// Reference: https://wg21.link/alg.partitions#:~:text=ranges::partition_point(R
+template <typename Range,
+          typename Pred,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto partition_point(Range&& range, Pred pred, Proj proj = {}) {
+  return ranges::partition_point(ranges::begin(range), ranges::end(range),
+                                 std::move(pred), std::move(proj));
+}
+
+// [alg.merge] Merge
+// Reference: https://wg21.link/alg.merge
+
+// Let `N` be `(last1 - first1) + (last2 - first2)`.
+//
+// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted
+// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting
+// range does not overlap with either of the original ranges.
+//
+// Effects: Copies all the elements of the two ranges `[first1, last1)` and
+// `[first2, last2)` into the range `[result, result_last)`, where `result_last`
+// is `result + N`. If an element `a` precedes `b` in an input range, `a` is
+// copied into the output range before `b`. If `e1` is an element of
+// `[first1, last1)` and `e2` of `[first2, last2)`, `e2` is copied into the
+// output range before `e1` if and only if
+// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`.
+//
+// Returns: `result_last`.
+//
+// Complexity: At most `N - 1` comparisons and applications of each projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(I1
+template <typename InputIterator1,
+          typename InputIterator2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<InputIterator1>,
+          typename = internal::iterator_category_t<InputIterator2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator1, Proj1>,
+                                       projected<InputIterator2, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator2, Proj2>,
+                                       projected<InputIterator1, Proj1>>>
+constexpr auto merge(InputIterator1 first1,
+                     InputIterator1 last1,
+                     InputIterator2 first2,
+                     InputIterator2 last2,
+                     OutputIterator result,
+                     Comp comp = {},
+                     Proj1 proj1 = {},
+                     Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::merge expects
+  // comp(proj2(lhs), proj1(rhs)) to compile.
+  return std::merge(
+      first1, last1, first2, last2, result,
+      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
+}
+
+// Let `N` be `size(range1) + size(range2)`.
+//
+// Preconditions: The ranges `range1` and `range2` are sorted with respect to
+// `comp` and `proj1` or `proj2`, respectively. The resulting range does not
+// overlap with either of the original ranges.
+//
+// Effects: Copies all the elements of the two ranges `range1` and `range2` into
+// the range `[result, result_last)`, where `result_last` is `result + N`. If an
+// element `a` precedes `b` in an input range, `a` is copied into the output
+// range before `b`. If `e1` is an element of `range1` and `e2` of `range2`,
+// `e2` is copied into the output range before `e1` if and only if
+// `bool(invoke(comp, invoke(proj2, e2), invoke(proj1, e1)))` is `true`.
+//
+// Returns: `result_last`.
+//
+// Complexity: At most `N - 1` comparisons and applications of each projection.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.merge#:~:text=ranges::merge(R1
+template <typename Range1,
+          typename Range2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr auto merge(Range1&& range1,
+                     Range2&& range2,
+                     OutputIterator result,
+                     Comp comp = {},
+                     Proj1 proj1 = {},
+                     Proj2 proj2 = {}) {
+  return ranges::merge(ranges::begin(range1), ranges::end(range1),
+                       ranges::begin(range2), ranges::end(range2), result,
+                       std::move(comp), std::move(proj1), std::move(proj2));
+}
+
+// Preconditions: `[first, middle)` and `[middle, last)` are valid ranges sorted
+// with respect to `comp` and `proj`.
+//
+// Effects: Merges two sorted consecutive ranges `[first, middle)` and
+// `[middle, last)`, putting the result of the merge into the range
+// `[first, last)`. The resulting range is sorted with respect to `comp` and
+// `proj`.
+//
+// Returns: `last`.
+//
+// Complexity: Let `N = last - first`: If enough additional memory is available,
+// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either
+// case, twice as many projections as comparisons.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(I
+template <typename BidirectionalIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<BidirectionalIterator>>
+constexpr auto inplace_merge(BidirectionalIterator first,
+                             BidirectionalIterator middle,
+                             BidirectionalIterator last,
+                             Comp comp = {},
+                             Proj proj = {}) {
+  std::inplace_merge(first, middle, last,
+                     internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Preconditions: `[begin(range), middle)` and `[middle, end(range))` are valid
+// ranges sorted with respect to `comp` and `proj`.
+//
+// Effects: Merges two sorted consecutive ranges `[begin(range), middle)` and
+// `[middle, end(range))`, putting the result of the merge into `range`. The
+// resulting range is sorted with respect to `comp` and `proj`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: Let `N = size(range)`: If enough additional memory is available,
+// exactly `N - 1` comparisons. Otherwise, `O(N log N)` comparisons. In either
+// case, twice as many projections as comparisons.
+//
+// Remarks: Stable.
+//
+// Reference: https://wg21.link/alg.merge#:~:text=ranges::inplace_merge(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto inplace_merge(Range&& range,
+                             iterator_t<Range> middle,
+                             Comp comp = {},
+                             Proj proj = {}) {
+  return ranges::inplace_merge(ranges::begin(range), middle, ranges::end(range),
+                               std::move(comp), std::move(proj));
+}
+
+// [alg.set.operations] Set operations on sorted structures
+// Reference: https://wg21.link/alg.set.operations
+
+// [includes] includes
+// Reference: https://wg21.link/includes
+
+// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted
+// with respect to `comp` and `proj1` or `proj2`, respectively.
+//
+// Returns: `true` if and only if `[first2, last2)` is a subsequence of
+// `[first1, last1)`.
+//
+// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`
+// comparisons and applications of each projection.
+//
+// Reference: https://wg21.link/includes#:~:text=ranges::includes(I1
+template <typename InputIterator1,
+          typename InputIterator2,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<InputIterator1>,
+          typename = internal::iterator_category_t<InputIterator2>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator1, Proj1>,
+                                       projected<InputIterator2, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator2, Proj2>,
+                                       projected<InputIterator1, Proj1>>>
+constexpr auto includes(InputIterator1 first1,
+                        InputIterator1 last1,
+                        InputIterator2 first2,
+                        InputIterator2 last2,
+                        Comp comp = {},
+                        Proj1 proj1 = {},
+                        Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::includes expects
+  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.
+  return std::includes(
+      first1, last1, first2, last2,
+      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
+}
+
+// Preconditions: The ranges `range1` and `range2` are sorted with respect to
+// `comp` and `proj1` or `proj2`, respectively.
+//
+// Returns: `true` if and only if `range2` is a subsequence of `range1`.
+//
+// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and
+// applications of each projection.
+//
+// Reference: https://wg21.link/includes#:~:text=ranges::includes(R1
+template <typename Range1,
+          typename Range2,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr auto includes(Range1&& range1,
+                        Range2&& range2,
+                        Comp comp = {},
+                        Proj1 proj1 = {},
+                        Proj2 proj2 = {}) {
+  return ranges::includes(ranges::begin(range1), ranges::end(range1),
+                          ranges::begin(range2), ranges::end(range2),
+                          std::move(comp), std::move(proj1), std::move(proj2));
+}
+
+// [set.union] set_union
+// Reference: https://wg21.link/set.union
+
+// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted
+// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting
+// range does not overlap with either of the original ranges.
+//
+// Effects: Constructs a sorted union of the elements from the two ranges; that
+// is, the set of elements that are present in one or both of the ranges.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`
+// comparisons and applications of each projection.
+//
+// Remarks: Stable. If `[first1, last1)` contains `m` elements that are
+// equivalent to each other and `[first2, last2)` contains `n` elements that are
+// equivalent to them, then all `m` elements from the first range are copied to
+// the output range, in order, and then the final `max(n - m , 0)` elements from
+// the second range are copied to the output range, in order.
+//
+// Reference: https://wg21.link/set.union#:~:text=ranges::set_union(I1
+template <typename InputIterator1,
+          typename InputIterator2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<InputIterator1>,
+          typename = internal::iterator_category_t<InputIterator2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator1, Proj1>,
+                                       projected<InputIterator2, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator2, Proj2>,
+                                       projected<InputIterator1, Proj1>>>
+constexpr auto set_union(InputIterator1 first1,
+                         InputIterator1 last1,
+                         InputIterator2 first2,
+                         InputIterator2 last2,
+                         OutputIterator result,
+                         Comp comp = {},
+                         Proj1 proj1 = {},
+                         Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::set_union expects
+  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.
+  return std::set_union(
+      first1, last1, first2, last2, result,
+      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
+}
+
+// Preconditions: The ranges `range1` and `range2` are sorted with respect to
+// `comp` and `proj1` or `proj2`, respectively. The resulting range does not
+// overlap with either of the original ranges.
+//
+// Effects: Constructs a sorted union of the elements from the two ranges; that
+// is, the set of elements that are present in one or both of the ranges.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and
+// applications of each projection.
+//
+// Remarks: Stable. If `range1` contains `m` elements that are equivalent to
+// each other and `range2` contains `n` elements that are equivalent to them,
+// then all `m` elements from the first range are copied to the output range, in
+// order, and then the final `max(n - m , 0)` elements from the second range are
+// copied to the output range, in order.
+//
+// Reference: https://wg21.link/set.union#:~:text=ranges::set_union(R1
+template <typename Range1,
+          typename Range2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr auto set_union(Range1&& range1,
+                         Range2&& range2,
+                         OutputIterator result,
+                         Comp comp = {},
+                         Proj1 proj1 = {},
+                         Proj2 proj2 = {}) {
+  return ranges::set_union(ranges::begin(range1), ranges::end(range1),
+                           ranges::begin(range2), ranges::end(range2), result,
+                           std::move(comp), std::move(proj1), std::move(proj2));
+}
+
+// [set.intersection] set_intersection
+// Reference: https://wg21.link/set.intersection
+
+// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted
+// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting
+// range does not overlap with either of the original ranges.
+//
+// Effects: Constructs a sorted intersection of the elements from the two
+// ranges; that is, the set of elements that are present in both of the ranges.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`
+// comparisons and applications of each projection.
+//
+// Remarks: Stable. If `[first1, last1)` contains `m` elements that are
+// equivalent to each other and `[first2, last2)` contains `n` elements that are
+// equivalent to them, the first `min(m, n)` elements are copied from the first
+// range to the output range, in order.
+//
+// Reference:
+// https://wg21.link/set.intersection#:~:text=ranges::set_intersection(I1
+template <typename InputIterator1,
+          typename InputIterator2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<InputIterator1>,
+          typename = internal::iterator_category_t<InputIterator2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator1, Proj1>,
+                                       projected<InputIterator2, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator2, Proj2>,
+                                       projected<InputIterator1, Proj1>>>
+constexpr auto set_intersection(InputIterator1 first1,
+                                InputIterator1 last1,
+                                InputIterator2 first2,
+                                InputIterator2 last2,
+                                OutputIterator result,
+                                Comp comp = {},
+                                Proj1 proj1 = {},
+                                Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::set_intersection expects
+  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.
+  return std::set_intersection(
+      first1, last1, first2, last2, result,
+      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
+}
+
+// Preconditions: The ranges `range1` and `range2` are sorted with respect to
+// `comp` and `proj1` or `proj2`, respectively. The resulting range does not
+// overlap with either of the original ranges.
+//
+// Effects: Constructs a sorted intersection of the elements from the two
+// ranges; that is, the set of elements that are present in both of the ranges.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and
+// applications of each projection.
+//
+// Remarks: Stable. If `range1` contains `m` elements that are equivalent to
+// each other and `range2` contains `n` elements that are equivalent to them,
+// the first `min(m, n)` elements are copied from the first range to the output
+// range, in order.
+//
+// Reference:
+// https://wg21.link/set.intersection#:~:text=ranges::set_intersection(R1
+template <typename Range1,
+          typename Range2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr auto set_intersection(Range1&& range1,
+                                Range2&& range2,
+                                OutputIterator result,
+                                Comp comp = {},
+                                Proj1 proj1 = {},
+                                Proj2 proj2 = {}) {
+  return ranges::set_intersection(ranges::begin(range1), ranges::end(range1),
+                                  ranges::begin(range2), ranges::end(range2),
+                                  result, std::move(comp), std::move(proj1),
+                                  std::move(proj2));
+}
+
+// [set.difference] set_difference
+// Reference: https://wg21.link/set.difference
+
+// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted
+// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting
+// range does not overlap with either of the original ranges.
+//
+// Effects: Copies the elements of the range `[first1, last1)` which are not
+// present in the range `[first2, last2)` to the range beginning at `result`.
+// The elements in the constructed range are sorted.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`
+// comparisons and applications of each projection.
+//
+// Remarks: If `[first1, last1)` contains `m` elements that are equivalent to
+// each other and `[first2, last2)` contains `n` elements that are equivalent to
+// them, the last `max(m - n, 0)` elements from `[first1, last1)` are copied to
+// the output range, in order.
+//
+// Reference:
+// https://wg21.link/set.difference#:~:text=ranges::set_difference(I1
+template <typename InputIterator1,
+          typename InputIterator2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<InputIterator1>,
+          typename = internal::iterator_category_t<InputIterator2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator1, Proj1>,
+                                       projected<InputIterator2, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator2, Proj2>,
+                                       projected<InputIterator1, Proj1>>>
+constexpr auto set_difference(InputIterator1 first1,
+                              InputIterator1 last1,
+                              InputIterator2 first2,
+                              InputIterator2 last2,
+                              OutputIterator result,
+                              Comp comp = {},
+                              Proj1 proj1 = {},
+                              Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::set_difference expects
+  // comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to compile.
+  return std::set_difference(
+      first1, last1, first2, last2, result,
+      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
+}
+
+// Preconditions: The ranges `range1` and `range2` are sorted with respect to
+// `comp` and `proj1` or `proj2`, respectively. The resulting range does not
+// overlap with either of the original ranges.
+//
+// Effects: Copies the elements of `range1` which are not present in `range2`
+// to the range beginning at `result`. The elements in the constructed range are
+// sorted.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and
+// applications of each projection.
+//
+// Remarks: Stable. If `range1` contains `m` elements that are equivalent to
+// each other and `range2` contains `n` elements that are equivalent to them,
+// the last `max(m - n, 0)` elements from `range1` are copied to the output
+// range, in order.
+//
+// Reference:
+// https://wg21.link/set.difference#:~:text=ranges::set_difference(R1
+template <typename Range1,
+          typename Range2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr auto set_difference(Range1&& range1,
+                              Range2&& range2,
+                              OutputIterator result,
+                              Comp comp = {},
+                              Proj1 proj1 = {},
+                              Proj2 proj2 = {}) {
+  return ranges::set_difference(ranges::begin(range1), ranges::end(range1),
+                                ranges::begin(range2), ranges::end(range2),
+                                result, std::move(comp), std::move(proj1),
+                                std::move(proj2));
+}
+
+// [set.symmetric.difference] set_symmetric_difference
+// Reference: https://wg21.link/set.symmetric.difference
+
+// Preconditions: The ranges `[first1, last1)` and `[first2, last2)` are sorted
+// with respect to `comp` and `proj1` or `proj2`, respectively. The resulting
+// range does not overlap with either of the original ranges.
+//
+// Effects: Copies the elements of the range `[first1, last1)` that are not
+// present in the range `[first2, last2)`, and the elements of the range
+// `[first2, last2)` that are not present in the range `[first1, last1)` to the
+// range beginning at `result`. The elements in the constructed range are
+// sorted.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * ((last1 - first1) + (last2 - first2)) - 1`
+// comparisons and applications of each projection.
+//
+// Remarks: Stable. If `[first1, last1)` contains `m` elements that are
+// equivalent to each other and `[first2, last2)` contains `n` elements that are
+// equivalent to them, then `|m - n|` of those elements shall be copied to the
+// output range: the last `m - n` of these elements from `[first1, last1)` if
+// `m > n`, and the last `n - m` of these elements from `[first2, last2)` if
+// `m < n`. In either case, the elements are copied in order.
+//
+// Reference:
+// https://wg21.link/set.symmetric.difference#:~:text=set_symmetric_difference(I1
+template <typename InputIterator1,
+          typename InputIterator2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<InputIterator1>,
+          typename = internal::iterator_category_t<InputIterator2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator1, Proj1>,
+                                       projected<InputIterator2, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<InputIterator2, Proj2>,
+                                       projected<InputIterator1, Proj1>>>
+constexpr auto set_symmetric_difference(InputIterator1 first1,
+                                        InputIterator1 last1,
+                                        InputIterator2 first2,
+                                        InputIterator2 last2,
+                                        OutputIterator result,
+                                        Comp comp = {},
+                                        Proj1 proj1 = {},
+                                        Proj2 proj2 = {}) {
+  // Needs to opt-in to all permutations, since std::set_symmetric_difference
+  // expects comp(proj1(lhs), proj2(rhs)) and comp(proj2(lhs), proj1(rhs)) to
+  // compile.
+  return std::set_symmetric_difference(
+      first1, last1, first2, last2, result,
+      internal::PermutedProjectedBinaryPredicate(comp, proj1, proj2));
+}
+
+// Preconditions: The ranges `range1` and `range2` are sorted with respect to
+// `comp` and `proj1` or `proj2`, respectively. The resulting range does not
+// overlap with either of the original ranges.
+//
+// Effects: Copies the elements of `range1` that are not present in `range2`,
+// and the elements of `range2` that are not present in `range1` to the range
+// beginning at `result`. The elements in the constructed range are sorted.
+//
+// Returns: The end of the constructed range.
+//
+// Complexity: At most `2 * (size(range1) + size(range2)) - 1` comparisons and
+// applications of each projection.
+//
+// Remarks: Stable. If `range1` contains `m` elements that are equivalent to
+// each other and `range2` contains `n` elements that are equivalent to them,
+// then `|m - n|` of those elements shall be copied to the output range: the
+// last `m - n` of these elements from `range1` if `m > n`, and the last `n - m`
+// of these elements from `range2` if `m < n`. In either case, the elements are
+// copied in order.
+//
+// Reference:
+// https://wg21.link/set.symmetric.difference#:~:text=set_symmetric_difference(R1
+template <typename Range1,
+          typename Range2,
+          typename OutputIterator,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = internal::iterator_category_t<OutputIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr auto set_symmetric_difference(Range1&& range1,
+                                        Range2&& range2,
+                                        OutputIterator result,
+                                        Comp comp = {},
+                                        Proj1 proj1 = {},
+                                        Proj2 proj2 = {}) {
+  return ranges::set_symmetric_difference(
+      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),
+      ranges::end(range2), result, std::move(comp), std::move(proj1),
+      std::move(proj2));
+}
+
+// [alg.heap.operations] Heap operations
+// Reference: https://wg21.link/alg.heap.operations
+
+// [push.heap] push_heap
+// Reference: https://wg21.link/push.heap
+
+// Preconditions: The range `[first, last - 1)` is a valid heap with respect to
+// `comp` and `proj`.
+//
+// Effects: Places the value in the location `last - 1` into the resulting heap
+// `[first, last)`.
+//
+// Returns: `last`.
+//
+// Complexity: At most `log(last - first)` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/push.heap#:~:text=ranges::push_heap(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto push_heap(RandomAccessIterator first,
+                         RandomAccessIterator last,
+                         Comp comp = {},
+                         Proj proj = {}) {
+  std::push_heap(first, last,
+                 internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Preconditions: The range `[begin(range), end(range) - 1)` is a valid heap
+// with respect to `comp` and `proj`.
+//
+// Effects: Places the value in the location `end(range) - 1` into the resulting
+// heap `range`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: At most `log(size(range))` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/push.heap#:~:text=ranges::push_heap(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto push_heap(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::push_heap(ranges::begin(range), ranges::end(range),
+                           std::move(comp), std::move(proj));
+}
+
+// [pop.heap] pop_heap
+// Reference: https://wg21.link/pop.heap
+
+// Preconditions: The range `[first, last)` is a valid non-empty heap with
+// respect to `comp` and `proj`.
+//
+// Effects: Swaps the value in the location `first` with the value in the
+// location `last - 1` and makes `[first, last - 1)` into a heap with respect to
+// `comp` and `proj`.
+//
+// Returns: `last`.
+//
+// Complexity: At most `2 log(last - first)` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/pop.heap#:~:text=ranges::pop_heap(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto pop_heap(RandomAccessIterator first,
+                        RandomAccessIterator last,
+                        Comp comp = {},
+                        Proj proj = {}) {
+  std::pop_heap(first, last,
+                internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Preconditions: `range` is a valid non-empty heap with respect to `comp` and
+// `proj`.
+//
+// Effects: Swaps the value in the location `begin(range)` with the value in the
+// location `end(range) - 1` and makes `[begin(range), end(range) - 1)` into a
+// heap with respect to `comp` and `proj`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: At most `2 log(size(range))` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/pop.heap#:~:text=ranges::pop_heap(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto pop_heap(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::pop_heap(ranges::begin(range), ranges::end(range),
+                          std::move(comp), std::move(proj));
+}
+
+// [make.heap] make_heap
+// Reference: https://wg21.link/make.heap
+
+// Effects: Constructs a heap with respect to `comp` and `proj` out of the range
+// `[first, last)`.
+//
+// Returns: `last`.
+//
+// Complexity: At most `3 log(last - first)` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/make.heap#:~:text=ranges::make_heap(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto make_heap(RandomAccessIterator first,
+                         RandomAccessIterator last,
+                         Comp comp = {},
+                         Proj proj = {}) {
+  std::make_heap(first, last,
+                 internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Effects: Constructs a heap with respect to `comp` and `proj` out of `range`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: At most `3 log(size(range))` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/make.heap#:~:text=ranges::make_heap(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto make_heap(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::make_heap(ranges::begin(range), ranges::end(range),
+                           std::move(comp), std::move(proj));
+}
+
+// [sort.heap] sort_heap
+// Reference: https://wg21.link/sort.heap
+
+// Preconditions: The range `[first, last)` is a valid heap with respect to
+// `comp` and `proj`.
+//
+// Effects: Sorts elements in the heap `[first, last)` with respect to `comp`
+// and `proj`.
+//
+// Returns: `last`.
+//
+// Complexity: At most `2 N log N` comparisons, where `N = last - first`, and
+// twice as many projections.
+//
+// Reference: https://wg21.link/sort.heap#:~:text=ranges::sort_heap(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto sort_heap(RandomAccessIterator first,
+                         RandomAccessIterator last,
+                         Comp comp = {},
+                         Proj proj = {}) {
+  std::sort_heap(first, last,
+                 internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return last;
+}
+
+// Preconditions: `range` is a valid heap with respect to `comp` and `proj`.
+//
+// Effects: Sorts elements in the heap `range` with respect to `comp` and
+// `proj`.
+//
+// Returns: `end(range)`.
+//
+// Complexity: At most `2 N log N` comparisons, where `N = size(range)`, and
+// twice as many projections.
+//
+// Reference: https://wg21.link/sort.heap#:~:text=ranges::sort_heap(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto sort_heap(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::sort_heap(ranges::begin(range), ranges::end(range),
+                           std::move(comp), std::move(proj));
+}
+
+// [is.heap] is_heap
+// Reference: https://wg21.link/is.heap
+
+// Returns: Whether the range `[first, last)` is a heap with respect to `comp`
+// and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto is_heap(RandomAccessIterator first,
+                       RandomAccessIterator last,
+                       Comp comp = {},
+                       Proj proj = {}) {
+  return std::is_heap(first, last,
+                      internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Returns: Whether `range` is a heap with respect to `comp` and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto is_heap(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::is_heap(ranges::begin(range), ranges::end(range),
+                         std::move(comp), std::move(proj));
+}
+
+// Returns: The last iterator `i` in `[first, last]` for which the range
+// `[first, i)` is a heap with respect to `comp` and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap_until(I
+template <typename RandomAccessIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<RandomAccessIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<RandomAccessIterator, Proj>,
+                                       projected<RandomAccessIterator, Proj>>>
+constexpr auto is_heap_until(RandomAccessIterator first,
+                             RandomAccessIterator last,
+                             Comp comp = {},
+                             Proj proj = {}) {
+  return std::is_heap_until(
+      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Returns: The last iterator `i` in `[begin(range), end(range)]` for which the
+// range `[begin(range), i)` is a heap with respect to `comp` and `proj`.
+//
+// Complexity: Linear.
+//
+// Reference: https://wg21.link/is.heap#:~:text=ranges::is_heap_until(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto is_heap_until(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::is_heap_until(ranges::begin(range), ranges::end(range),
+                               std::move(comp), std::move(proj));
+}
+
+// [alg.min.max] Minimum and maximum
+// Reference: https://wg21.link/alg.min.max
+
+// Returns: The smaller value. Returns the first argument when the arguments are
+// equivalent.
+//
+// Complexity: Exactly one comparison and two applications of the projection, if
+// any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min
+template <typename T, typename Comp = ranges::less, typename Proj = identity>
+constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}) {
+  return gurl_base::invoke(comp, gurl_base::invoke(proj, b), gurl_base::invoke(proj, a)) ? b
+                                                                          : a;
+}
+
+// Preconditions: `!empty(ilist)`.
+//
+// Returns: The smallest value in the input range. Returns a copy of the
+// leftmost element when several elements are equivalent to the smallest.
+//
+// Complexity: Exactly `size(ilist) - 1` comparisons and twice as many
+// applications of the projection, if any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min(initializer_list
+template <typename T, typename Comp = ranges::less, typename Proj = identity>
+constexpr T min(std::initializer_list<T> ilist,
+                Comp comp = {},
+                Proj proj = {}) {
+  return *std::min_element(
+      ilist.begin(), ilist.end(),
+      internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Preconditions: `!empty(range)`.
+//
+// Returns: The smallest value in the input range. Returns a copy of the
+// leftmost element when several elements are equivalent to the smallest.
+//
+// Complexity: Exactly `size(range) - 1` comparisons and twice as many
+// applications of the projection, if any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto min(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return *std::min_element(
+      ranges::begin(range), ranges::end(range),
+      internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Returns: The larger value. Returns the first argument when the arguments are
+// equivalent.
+//
+// Complexity: Exactly one comparison and two applications of the projection, if
+// any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max
+template <typename T, typename Comp = ranges::less, typename Proj = identity>
+constexpr const T& max(const T& a, const T& b, Comp comp = {}, Proj proj = {}) {
+  return gurl_base::invoke(comp, gurl_base::invoke(proj, a), gurl_base::invoke(proj, b)) ? b
+                                                                          : a;
+}
+
+// Preconditions: `!empty(ilist)`.
+//
+// Returns: The largest value in the input range. Returns a copy of the leftmost
+// element when several elements are equivalent to the largest.
+//
+// Complexity: Exactly `size(ilist) - 1` comparisons and twice as many
+// applications of the projection, if any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max(initializer_list
+template <typename T, typename Comp = ranges::less, typename Proj = identity>
+constexpr T max(std::initializer_list<T> ilist,
+                Comp comp = {},
+                Proj proj = {}) {
+  return *std::max_element(
+      ilist.begin(), ilist.end(),
+      internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Preconditions: `!empty(range)`.
+//
+// Returns: The largest value in the input range. Returns a copy of the leftmost
+// element when several elements are equivalent to the smallest.
+//
+// Complexity: Exactly `size(range) - 1` comparisons and twice as many
+// applications of the projection, if any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto max(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return *std::max_element(
+      ranges::begin(range), ranges::end(range),
+      internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Returns: `{b, a}` if `b` is smaller than `a`, and `{a, b}` otherwise.
+//
+// Complexity: Exactly one comparison and two applications of the projection, if
+// any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax
+template <typename T, typename Comp = ranges::less, typename Proj = identity>
+constexpr auto minmax(const T& a, const T& b, Comp comp = {}, Proj proj = {}) {
+  return std::minmax(a, b,
+                     internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Preconditions: `!empty(ilist)`.
+//
+// Returns: Let `X` be the return type. Returns `X{x, y}`, where `x` is a copy
+// of the leftmost element with the smallest value and `y` a copy of the
+// rightmost element with the largest value in the input range.
+//
+// Complexity: At most `(3/2) size(ilist)` applications of the corresponding
+// predicate and twice as many applications of the projection, if any.
+//
+// Reference:
+// https://wg21.link/alg.min.max#:~:text=ranges::minmax(initializer_list
+template <typename T, typename Comp = ranges::less, typename Proj = identity>
+constexpr auto minmax(std::initializer_list<T> ilist,
+                      Comp comp = {},
+                      Proj proj = {}) {
+  auto it =
+      std::minmax_element(ranges::begin(ilist), ranges::end(ilist),
+                          internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return std::pair<T, T>{*it.first, *it.second};
+}
+
+// Preconditions: `!empty(range)`.
+//
+// Returns: Let `X` be the return type. Returns `X{x, y}`, where `x` is a copy
+// of the leftmost element with the smallest value and `y` a copy of the
+// rightmost element with the largest value in the input range.
+//
+// Complexity: At most `(3/2) size(range)` applications of the corresponding
+// predicate and twice as many applications of the projection, if any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>>
+constexpr auto minmax(Range&& range, Comp comp = {}, Proj proj = {}) {
+  using T = range_value_t<Range>;
+  auto it =
+      std::minmax_element(ranges::begin(range), ranges::end(range),
+                          internal::ProjectedBinaryPredicate(comp, proj, proj));
+  return std::pair<T, T>{*it.first, *it.second};
+}
+
+// Returns: The first iterator i in the range `[first, last)` such that for
+// every iterator `j` in the range `[first, last)`,
+// `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is `false`. Returns
+// `last` if `first == last`.
+//
+// Complexity: Exactly `max(last - first - 1, 0)` comparisons and twice as
+// many projections.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min_element(I
+template <typename ForwardIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator, Proj>,
+                                       projected<ForwardIterator, Proj>>>
+constexpr auto min_element(ForwardIterator first,
+                           ForwardIterator last,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return std::min_element(first, last,
+                          internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Returns: The first iterator i in `range` such that for every iterator `j` in
+// `range`, `bool(invoke(comp, invoke(proj, *j), invoke(proj, *i)))` is `false`.
+// Returns `end(range)` if `empty(range)`.
+//
+// Complexity: Exactly `max(size(range) - 1, 0)` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::min_element(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto min_element(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::min_element(ranges::begin(range), ranges::end(range),
+                             std::move(comp), std::move(proj));
+}
+
+// Returns: The first iterator i in the range `[first, last)` such that for
+// every iterator `j` in the range `[first, last)`,
+// `bool(invoke(comp, invoke(proj, *i), invoke(proj, *j)))` is `false`.
+// Returns `last` if `first == last`.
+//
+// Complexity: Exactly `max(last - first - 1, 0)` comparisons and twice as
+// many projections.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max_element(I
+template <typename ForwardIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator, Proj>,
+                                       projected<ForwardIterator, Proj>>>
+constexpr auto max_element(ForwardIterator first,
+                           ForwardIterator last,
+                           Comp comp = {},
+                           Proj proj = {}) {
+  return std::max_element(first, last,
+                          internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Returns: The first iterator i in `range` such that for every iterator `j`
+// in `range`, `bool(invoke(comp, invoke(proj, *j), invoke(proj, *j)))` is
+// `false`. Returns `end(range)` if `empty(range)`.
+//
+// Complexity: Exactly `max(size(range) - 1, 0)` comparisons and twice as many
+// projections.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::max_element(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto max_element(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::max_element(ranges::begin(range), ranges::end(range),
+                             std::move(comp), std::move(proj));
+}
+
+// Returns: `{first, first}` if `[first, last)` is empty, otherwise `{m, M}`,
+// where `m` is the first iterator in `[first, last)` such that no iterator in
+// the range refers to a smaller element, and where `M` is the last iterator
+// in
+// `[first, last)` such that no iterator in the range refers to a larger
+// element.
+//
+// Complexity: Let `N` be `last - first`. At most `max(3/2 (N − 1), 0)`
+// comparisons and twice as many applications of the projection, if any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax_element(I
+template <typename ForwardIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<ForwardIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator, Proj>,
+                                       projected<ForwardIterator, Proj>>>
+constexpr auto minmax_element(ForwardIterator first,
+                              ForwardIterator last,
+                              Comp comp = {},
+                              Proj proj = {}) {
+  return std::minmax_element(
+      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Returns: `{begin(range), begin(range)}` if `range` is empty, otherwise
+// `{m, M}`, where `m` is the first iterator in `range` such that no iterator
+// in the range refers to a smaller element, and where `M` is the last
+// iterator in `range` such that no iterator in the range refers to a larger
+// element.
+//
+// Complexity: Let `N` be `size(range)`. At most `max(3/2 (N − 1), 0)`
+// comparisons and twice as many applications of the projection, if any.
+//
+// Reference: https://wg21.link/alg.min.max#:~:text=ranges::minmax_element(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto minmax_element(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::minmax_element(ranges::begin(range), ranges::end(range),
+                                std::move(comp), std::move(proj));
+}
+
+// [alg.clamp] Bounded value
+// Reference: https://wg21.link/alg.clamp
+
+// Preconditions: `bool(invoke(comp, invoke(proj, hi), invoke(proj, lo)))` is
+// `false`.
+//
+// Returns: `lo` if `bool(invoke(comp, invoke(proj, v), invoke(proj, lo)))` is
+// `true`, `hi` if `bool(invoke(comp, invoke(proj, hi), invoke(proj, v)))` is
+// `true`, otherwise `v`.
+//
+// Complexity: At most two comparisons and three applications of the
+// projection.
+//
+// Reference: https://wg21.link/alg.clamp#:~:text=ranges::clamp
+template <typename T, typename Comp = ranges::less, typename Proj = identity>
+constexpr const T& clamp(const T& v,
+                         const T& lo,
+                         const T& hi,
+                         Comp comp = {},
+                         Proj proj = {}) {
+  auto&& projected_v = gurl_base::invoke(proj, v);
+  if (gurl_base::invoke(comp, projected_v, gurl_base::invoke(proj, lo)))
+    return lo;
+
+  return gurl_base::invoke(comp, gurl_base::invoke(proj, hi), projected_v) ? hi : v;
+}
+
+// [alg.lex.comparison] Lexicographical comparison
+// Reference: https://wg21.link/alg.lex.comparison
+
+// Returns: `true` if and only if the sequence of elements defined by the range
+// `[first1, last1)` is lexicographically less than the sequence of elements
+// defined by the range `[first2, last2)`.
+//
+// Complexity: At most `2 min(last1 - first1, last2 - first2)` applications of
+// the corresponding comparison and each projection, if any.
+//
+// Remarks: If two sequences have the same number of elements and their
+// corresponding elements (if any) are equivalent, then neither sequence is
+// lexicographically less than the other. If one sequence is a proper prefix of
+// the other, then the shorter sequence is lexicographically less than the
+// longer sequence. Otherwise, the lexicographical comparison of the sequences
+// yields the same result as the comparison of the first corresponding pair of
+// elements that are not equivalent.
+//
+// Reference:
+// https://wg21.link/alg.lex.comparison#:~:text=lexicographical_compare(I1
+template <typename ForwardIterator1,
+          typename ForwardIterator2,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::iterator_category_t<ForwardIterator1>,
+          typename = internal::iterator_category_t<ForwardIterator2>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator1, Proj1>,
+                                       projected<ForwardIterator2, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<ForwardIterator2, Proj2>,
+                                       projected<ForwardIterator1, Proj1>>>
+constexpr bool lexicographical_compare(ForwardIterator1 first1,
+                                       ForwardIterator1 last1,
+                                       ForwardIterator2 first2,
+                                       ForwardIterator2 last2,
+                                       Comp comp = {},
+                                       Proj1 proj1 = {},
+                                       Proj2 proj2 = {}) {
+  for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
+    auto&& projected_first1 = gurl_base::invoke(proj1, *first1);
+    auto&& projected_first2 = gurl_base::invoke(proj2, *first2);
+    if (gurl_base::invoke(comp, projected_first1, projected_first2))
+      return true;
+    if (gurl_base::invoke(comp, projected_first2, projected_first1))
+      return false;
+  }
+
+  // `first2 != last2` is equivalent to `first1 == last1 && first2 != last2`
+  // here, since we broke out of the loop above.
+  return first2 != last2;
+}
+
+// Returns: `true` if and only if the sequence of elements defined by `range1`
+//  is lexicographically less than the sequence of elements defined by `range2`.
+//
+// Complexity: At most `2 min(size(range1), size(range2))` applications of the
+// corresponding comparison and each projection, if any.
+//
+// Remarks: If two sequences have the same number of elements and their
+// corresponding elements (if any) are equivalent, then neither sequence is
+// lexicographically less than the other. If one sequence is a proper prefix of
+// the other, then the shorter sequence is lexicographically less than the
+// longer sequence. Otherwise, the lexicographical comparison of the sequences
+// yields the same result as the comparison of the first corresponding pair of
+// elements that are not equivalent.
+//
+// Reference:
+// https://wg21.link/alg.lex.comparison#:~:text=lexicographical_compare(R1
+template <typename Range1,
+          typename Range2,
+          typename Comp = ranges::less,
+          typename Proj1 = identity,
+          typename Proj2 = identity,
+          typename = internal::range_category_t<Range1>,
+          typename = internal::range_category_t<Range2>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range1>, Proj1>,
+                                       projected<iterator_t<Range2>, Proj2>>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range2>, Proj2>,
+                                       projected<iterator_t<Range1>, Proj1>>>
+constexpr bool lexicographical_compare(Range1&& range1,
+                                       Range2&& range2,
+                                       Comp comp = {},
+                                       Proj1 proj1 = {},
+                                       Proj2 proj2 = {}) {
+  return ranges::lexicographical_compare(
+      ranges::begin(range1), ranges::end(range1), ranges::begin(range2),
+      ranges::end(range2), std::move(comp), std::move(proj1), std::move(proj2));
+}
+
+// [alg.permutation.generators] Permutation generators
+// Reference: https://wg21.link/alg.permutation.generators
+
+// Effects: Takes a sequence defined by the range `[first, last)` and transforms
+// it into the next permutation. The next permutation is found by assuming that
+// the set of all permutations is lexicographically sorted with respect to
+// `comp` and `proj`. If no such permutation exists, transforms the sequence
+// into the first permutation; that is, the ascendingly-sorted one.
+//
+// Returns: `true` if a next permutation was found and otherwise `false`.
+//
+// Complexity: At most `(last - first) / 2` swaps.
+//
+// Reference:
+// https://wg21.link/alg.permutation.generators#:~:text=next_permutation(I
+template <typename BidirectionalIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<BidirectionalIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<BidirectionalIterator, Proj>,
+                                       projected<BidirectionalIterator, Proj>>>
+constexpr auto next_permutation(BidirectionalIterator first,
+                                BidirectionalIterator last,
+                                Comp comp = {},
+                                Proj proj = {}) {
+  return std::next_permutation(
+      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Effects: Takes a sequence defined by `range` and transforms it into the next
+// permutation. The next permutation is found by assuming that the set of all
+// permutations is lexicographically sorted with respect to `comp` and `proj`.
+// If no such permutation exists, transforms the sequence into the first
+// permutation; that is, the ascendingly-sorted one.
+//
+// Returns: `true` if a next permutation was found and otherwise `false`.
+//
+// Complexity: At most `size(range) / 2` swaps.
+//
+// Reference:
+// https://wg21.link/alg.permutation.generators#:~:text=next_permutation(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto next_permutation(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::next_permutation(ranges::begin(range), ranges::end(range),
+                                  std::move(comp), std::move(proj));
+}
+
+// Effects: Takes a sequence defined by the range `[first, last)` and transforms
+// it into the previous permutation. The previous permutation is found by
+// assuming that the set of all permutations is lexicographically sorted with
+// respect to `comp` and `proj`. If no such permutation exists, transforms the
+// sequence into the last permutation; that is, the decreasingly-sorted one.
+//
+// Returns: `true` if a next permutation was found and otherwise `false`.
+//
+// Complexity: At most `(last - first) / 2` swaps.
+//
+// Reference:
+// https://wg21.link/alg.permutation.generators#:~:text=prev_permutation(I
+template <typename BidirectionalIterator,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::iterator_category_t<BidirectionalIterator>,
+          typename = indirect_result_t<Comp&,
+                                       projected<BidirectionalIterator, Proj>,
+                                       projected<BidirectionalIterator, Proj>>>
+constexpr auto prev_permutation(BidirectionalIterator first,
+                                BidirectionalIterator last,
+                                Comp comp = {},
+                                Proj proj = {}) {
+  return std::prev_permutation(
+      first, last, internal::ProjectedBinaryPredicate(comp, proj, proj));
+}
+
+// Effects: Takes a sequence defined by `range` and transforms it into the
+// previous permutation. The previous permutation is found by assuming that the
+// set of all permutations is lexicographically sorted with respect to `comp`
+// and `proj`. If no such permutation exists, transforms the sequence into the
+// last permutation; that is, the decreasingly-sorted one.
+//
+// Returns: `true` if a previous permutation was found and otherwise `false`.
+//
+// Complexity: At most `size(range) / 2` swaps.
+//
+// Reference:
+// https://wg21.link/alg.permutation.generators#:~:text=prev_permutation(R
+template <typename Range,
+          typename Comp = ranges::less,
+          typename Proj = identity,
+          typename = internal::range_category_t<Range>,
+          typename = indirect_result_t<Comp&,
+                                       projected<iterator_t<Range>, Proj>,
+                                       projected<iterator_t<Range>, Proj>>>
+constexpr auto prev_permutation(Range&& range, Comp comp = {}, Proj proj = {}) {
+  return ranges::prev_permutation(ranges::begin(range), ranges::end(range),
+                                  std::move(comp), std::move(proj));
+}
+
+}  // namespace ranges
+
+}  // namespace base
+
+#endif  // BASE_RANGES_ALGORITHM_H_
diff --git a/base/ranges/functional.h b/base/ranges/functional.h
new file mode 100644
index 0000000..e71abab
--- /dev/null
+++ b/base/ranges/functional.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_RANGES_FUNCTIONAL_H_
+#define BASE_RANGES_FUNCTIONAL_H_
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+namespace gurl_base {
+
+namespace ranges {
+
+// Simplified implementations of C++20's std::ranges comparison function
+// objects. As opposed to the std::ranges implementation, these versions do not
+// constrain the passed-in types.
+//
+// Reference: https://wg21.link/range.cmp
+using equal_to = std::equal_to<>;
+using not_equal_to = std::not_equal_to<>;
+using greater = std::greater<>;
+using less = std::less<>;
+using greater_equal = std::greater_equal<>;
+using less_equal = std::less_equal<>;
+
+}  // namespace ranges
+
+}  // namespace base
+
+#endif  // BASE_RANGES_FUNCTIONAL_H_
diff --git a/base/ranges/ranges.h b/base/ranges/ranges.h
new file mode 100644
index 0000000..7eef2d6
--- /dev/null
+++ b/base/ranges/ranges.h
@@ -0,0 +1,140 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_RANGES_RANGES_H_
+#define BASE_RANGES_RANGES_H_
+
+#include <array>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "base/template_util.h"
+
+namespace gurl_base {
+
+namespace internal {
+
+// Overload for C array.
+template <typename T, size_t N>
+constexpr T* begin(T (&array)[N], priority_tag<2>) {
+  return array;
+}
+
+// Overload for mutable std::array. Required since std::array::begin is not
+// constexpr prior to C++17. Needs to dispatch to the const overload since only
+// const operator[] is constexpr in C++14.
+template <typename T, size_t N>
+constexpr T* begin(std::array<T, N>& array, priority_tag<2> tag) {
+  return const_cast<T*>(begin(const_cast<const std::array<T, N>&>(array), tag));
+}
+
+// Overload for const std::array. Required since std::array::begin is not
+// constexpr prior to C++17.
+template <typename T, size_t N>
+constexpr const T* begin(const std::array<T, N>& array, priority_tag<2>) {
+  return N != 0 ? &array[0] : nullptr;
+}
+
+// Generic container overload.
+template <typename Range>
+constexpr auto begin(Range&& range, priority_tag<1>)
+    -> decltype(std::forward<Range>(range).begin()) {
+  return std::forward<Range>(range).begin();
+}
+
+// Overload for free begin() function.
+template <typename Range>
+constexpr auto begin(Range&& range, priority_tag<0>)
+    -> decltype(begin(std::forward<Range>(range))) {
+  return begin(std::forward<Range>(range));
+}
+
+// Overload for C array.
+template <typename T, size_t N>
+constexpr T* end(T (&array)[N], priority_tag<2>) {
+  return array + N;
+}
+
+// Overload for mutable std::array. Required since std::array::end is not
+// constexpr prior to C++17. Needs to dispatch to the const overload since only
+// const operator[] is constexpr in C++14.
+template <typename T, size_t N>
+constexpr T* end(std::array<T, N>& array, priority_tag<2> tag) {
+  return const_cast<T*>(end(const_cast<const std::array<T, N>&>(array), tag));
+}
+
+// Overload for const std::array. Required since std::array::end is not
+// constexpr prior to C++17.
+template <typename T, size_t N>
+constexpr const T* end(const std::array<T, N>& array, priority_tag<2>) {
+  return N != 0 ? (&array[0]) + N : nullptr;
+}
+
+// Generic container overload.
+template <typename Range>
+constexpr auto end(Range&& range, priority_tag<1>)
+    -> decltype(std::forward<Range>(range).end()) {
+  return std::forward<Range>(range).end();
+}
+
+// Overload for free end() function.
+template <typename Range>
+constexpr auto end(Range&& range, priority_tag<0>)
+    -> decltype(end(std::forward<Range>(range))) {
+  return end(std::forward<Range>(range));
+}
+
+}  // namespace internal
+
+namespace ranges {
+
+// Simplified implementation of C++20's std::ranges::begin.
+// As opposed to std::ranges::begin, this implementation does does not check
+// whether begin() returns an iterator and does not inhibit ADL.
+//
+// The trailing return type and dispatch to the internal implementation is
+// necessary to be SFINAE friendly.
+//
+// Reference: https://wg21.link/range.access.begin
+template <typename Range>
+constexpr auto begin(Range&& range) noexcept
+    -> decltype(internal::begin(std::forward<Range>(range),
+                                internal::priority_tag<2>())) {
+  return internal::begin(std::forward<Range>(range),
+                         internal::priority_tag<2>());
+}
+
+// Simplified implementation of C++20's std::ranges::end.
+// As opposed to std::ranges::end, this implementation does does not check
+// whether end() returns an iterator and does not inhibit ADL.
+//
+// The trailing return type and dispatch to the internal implementation is
+// necessary to be SFINAE friendly.
+//
+// Reference: - https://wg21.link/range.access.end
+template <typename Range>
+constexpr auto end(Range&& range) noexcept
+    -> decltype(internal::end(std::forward<Range>(range),
+                              internal::priority_tag<2>())) {
+  return internal::end(std::forward<Range>(range), internal::priority_tag<2>());
+}
+
+// Implementation of C++20's std::ranges::iterator_t.
+//
+// Reference: https://wg21.link/ranges.syn#:~:text=iterator_t
+template <typename Range>
+using iterator_t = decltype(ranges::begin(std::declval<Range&>()));
+
+// Implementation of C++20's std::ranges::range_value_t.
+//
+// Reference: https://wg21.link/ranges.syn#:~:text=range_value_t
+template <typename Range>
+using range_value_t = iter_value_t<iterator_t<Range>>;
+
+}  // namespace ranges
+
+}  // namespace base
+
+#endif  // BASE_RANGES_RANGES_H_
diff --git a/base/stl_util.h b/base/stl_util.h
index 14d2b6f..49fd272 100644
--- a/base/stl_util.h
+++ b/base/stl_util.h
@@ -171,6 +171,23 @@
 template <typename T>
 void as_const(const T&& t) = delete;
 
+// Simplified C++14 implementation of  C++20's std::to_address.
+// Note: This does not consider specializations of pointer_traits<>::to_address,
+// since that member function may only be present in C++20 and later.
+//
+// Reference: https://wg21.link/pointer.conversion#lib:to_address
+template <typename T>
+constexpr T* to_address(T* p) noexcept {
+  static_assert(!std::is_function<T>::value,
+                "Error: T must not be a function type.");
+  return p;
+}
+
+template <typename Ptr>
+constexpr auto to_address(const Ptr& p) noexcept {
+  return to_address(p.operator->());
+}
+
 // Returns a const reference to the underlying container of a container adapter.
 // Works for std::priority_queue, std::queue, and std::stack.
 template <class A>
@@ -447,10 +464,22 @@
                                   std::forward<Args>(args)...);
 }
 
-// Returns true if the container is sorted.
+// Returns true if the container is sorted. Requires constexpr std::begin/end,
+// which exists for arrays in C++14.
+// Note that std::is_sorted is constexpr beginning C++20 and this should be
+// switched to use it when C++20 is supported.
 template <typename Container>
-bool STLIsSorted(const Container& cont) {
-  return std::is_sorted(std::begin(cont), std::end(cont));
+constexpr bool STLIsSorted(const Container& cont) {
+  auto it = std::begin(cont);
+  const auto end = std::end(cont);
+  if (it == end)
+    return true;
+
+  for (auto prev = it++; it != end; prev = it++) {
+    if (*it < *prev)
+      return false;
+  }
+  return true;
 }
 
 // Returns a new ResultType containing the difference of two sorted containers.
@@ -701,6 +730,14 @@
   return optional.has_value() ? &optional.value() : nullptr;
 }
 
+// Helper for creating an Optional<T> from a potentially nullptr T*.
+template <class T>
+gurl_base::Optional<T> OptionalFromPtr(const T* value) {
+  if (value)
+    return gurl_base::Optional<T>(*value);
+  return gurl_base::nullopt;
+}
+
 }  // namespace base
 
 #endif  // BASE_STL_UTIL_H_
diff --git a/base/strings/abseil_string_conversions.cc b/base/strings/abseil_string_conversions.cc
new file mode 100644
index 0000000..e7c746e
--- /dev/null
+++ b/base/strings/abseil_string_conversions.cc
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/abseil_string_conversions.h"
+
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/ranges/algorithm.h"
+#include "base/strings/string_piece.h"
+#include "third_party/abseil-cpp/absl/strings/string_view.h"
+
+namespace gurl_base {
+
+std::vector<absl::string_view> StringPiecesToStringViews(
+    span<const StringPiece> pieces) {
+  std::vector<absl::string_view> views(pieces.size());
+  ranges::transform(pieces, views.begin(), &StringPieceToStringView);
+  return views;
+}
+
+std::vector<StringPiece> StringViewsToStringPieces(
+    span<const absl::string_view> views) {
+  std::vector<StringPiece> pieces(views.size());
+  ranges::transform(views, pieces.begin(), &StringViewToStringPiece);
+  return pieces;
+}
+
+}  // namespace base
diff --git a/base/strings/abseil_string_conversions.h b/base/strings/abseil_string_conversions.h
new file mode 100644
index 0000000..c821d93
--- /dev/null
+++ b/base/strings/abseil_string_conversions.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_ABSEIL_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_ABSEIL_STRING_CONVERSIONS_H_
+
+#include <vector>
+
+#include "polyfills/base/base_export.h"
+#include "base/containers/span.h"
+#include "base/strings/string_piece.h"
+#include "third_party/abseil-cpp/absl/strings/string_view.h"
+
+namespace gurl_base {
+
+// Converts `piece` to a string view, pointing to the same piece of memory.
+constexpr absl::string_view StringPieceToStringView(StringPiece piece) {
+  return {piece.data(), piece.size()};
+}
+
+// Converts `view` to a string piece, pointing to the same piece of memory.
+constexpr StringPiece StringViewToStringPiece(absl::string_view view) {
+  return {view.data(), view.size()};
+}
+
+// Converts `pieces` to string views, pointing to the same piece of memory.
+BASE_EXPORT std::vector<absl::string_view> StringPiecesToStringViews(
+    span<const StringPiece> pieces);
+
+// Converts `views` to string pieces, pointing to the same piece of memory.
+BASE_EXPORT std::vector<StringPiece> StringViewsToStringPieces(
+    span<const absl::string_view> views);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_ABSEIL_STRING_CONVERSIONS_H_
diff --git a/base/strings/abseil_string_conversions_unittest.cc b/base/strings/abseil_string_conversions_unittest.cc
new file mode 100644
index 0000000..e5f70f0
--- /dev/null
+++ b/base/strings/abseil_string_conversions_unittest.cc
@@ -0,0 +1,69 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/abseil_string_conversions.h"
+
+#include <vector>
+
+#include "base/containers/span.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_piece_forward.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/strings/string_view.h"
+
+namespace gurl_base {
+
+namespace {
+using string_view = absl::string_view;
+}  // namespace
+
+TEST(AbseilStringConversionsTest, StringPieceToStringView) {
+  static constexpr StringPiece kPiece = "foo";
+  static constexpr string_view kView = StringPieceToStringView(kPiece);
+  static_assert(kPiece.data() == kView.data(), "");
+  static_assert(kPiece.size() == kView.size(), "");
+}
+
+TEST(AbseilStringConversionsTest, StringViewToStringPiece) {
+  static constexpr string_view kView = "bar";
+  static constexpr StringPiece kPiece = StringViewToStringPiece(kView);
+  static_assert(kView.data() == kPiece.data(), "");
+  static_assert(kView.size() == kPiece.size(), "");
+}
+
+TEST(AbseilStringConversionsTest, StringPiecesToStringViews) {
+  static constexpr StringPiece kFoo = "foo";
+  static constexpr StringPiece kBar = "bar";
+  static constexpr StringPiece kBaz = "baz";
+
+  const std::vector<StringPiece> kPieces = {kFoo, kBar, kBaz};
+  const std::vector<string_view> kViews = StringPiecesToStringViews(kPieces);
+
+  ASSERT_EQ(kViews.size(), 3u);
+  EXPECT_EQ(kViews[0].data(), kFoo);
+  EXPECT_EQ(kViews[0].size(), 3u);
+  EXPECT_EQ(kViews[1].data(), kBar);
+  EXPECT_EQ(kViews[1].size(), 3u);
+  EXPECT_EQ(kViews[2].data(), kBaz);
+  EXPECT_EQ(kViews[2].size(), 3u);
+}
+
+TEST(AbseilStringConversionsTest, StringViewsToStringPieces) {
+  static constexpr string_view kFoo = "foo";
+  static constexpr string_view kBar = "bar";
+  static constexpr string_view kBaz = "baz";
+
+  const std::vector<string_view> kViews = {kFoo, kBar, kBaz};
+  const std::vector<StringPiece> kPieces = StringViewsToStringPieces(kViews);
+
+  ASSERT_EQ(kPieces.size(), 3u);
+  EXPECT_EQ(kPieces[0].data(), kFoo);
+  EXPECT_EQ(kPieces[0].size(), 3u);
+  EXPECT_EQ(kPieces[1].data(), kBar);
+  EXPECT_EQ(kPieces[1].size(), 3u);
+  EXPECT_EQ(kPieces[2].data(), kBaz);
+  EXPECT_EQ(kPieces[2].size(), 3u);
+}
+
+}  // namespace base
diff --git a/base/strings/escape.cc b/base/strings/escape.cc
new file mode 100644
index 0000000..cf46fca
--- /dev/null
+++ b/base/strings/escape.cc
@@ -0,0 +1,443 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/escape.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/third_party/icu/icu_utf.h"
+
+namespace gurl_base {
+
+namespace {
+
+// Contains nonzero when the corresponding character is unescapable for normal
+// URLs. These characters are the ones that may change the parsing of a URL, so
+// we don't want to unescape them sometimes. In many case we won't want to
+// unescape spaces, but that is controlled by parameters to Unescape*.
+//
+// The basic rule is that we can't unescape anything that would changing parsing
+// like # or ?. We also can't unescape &, =, or + since that could be part of a
+// query and that could change the server's parsing of the query. Nor can we
+// unescape \ since src/url/ will convert it to a /.
+//
+// Lastly, we can't unescape anything that doesn't have a canonical
+// representation in a URL. This means that unescaping will change the URL, and
+// you could get different behavior if you copy and paste the URL, or press
+// enter in the URL bar. The list of characters that fall into this category
+// are the ones labeled PASS (allow either escaped or unescaped) in the big
+// lookup table at the top of url/url_canon_path.cc.  Also, characters
+// that have CHAR_QUERY set in url/url_canon_internal.cc but are not
+// allowed in query strings according to http://www.ietf.org/rfc/rfc3261.txt are
+// not unescaped, to avoid turning a valid url according to spec into an
+// invalid one.
+// clang-format off
+const char kUrlUnescape[128] = {
+//   Null, control chars...
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+//  ' ' !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /
+     0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+//   0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?
+     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0,
+//   @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O
+     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+//   P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^  _
+     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
+//   `  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o
+     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+//   p  q  r  s  t  u  v  w  x  y  z  {  |  }  ~  <NBSP>
+     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
+};
+// clang-format on
+
+// Attempts to unescape the sequence at |index| within |escaped_text|.  If
+// successful, sets |value| to the unescaped value.  Returns whether
+// unescaping succeeded.
+bool UnescapeUnsignedByteAtIndex(StringPiece escaped_text,
+                                 size_t index,
+                                 unsigned char* value) {
+  if ((index + 2) >= escaped_text.size())
+    return false;
+  if (escaped_text[index] != '%')
+    return false;
+  char most_sig_digit(escaped_text[index + 1]);
+  char least_sig_digit(escaped_text[index + 2]);
+  if (IsHexDigit(most_sig_digit) && IsHexDigit(least_sig_digit)) {
+    *value =
+        HexDigitToInt(most_sig_digit) * 16 + HexDigitToInt(least_sig_digit);
+    return true;
+  }
+  return false;
+}
+
+// Attempts to unescape and decode a UTF-8-encoded percent-escaped character at
+// the specified index. On success, returns true, sets |code_point_out| to be
+// the character's code point and |unescaped_out| to be the unescaped UTF-8
+// string. |unescaped_out| will always be 1/3rd the length of the substring of
+// |escaped_text| that corresponds to the unescaped character.
+bool UnescapeUTF8CharacterAtIndex(StringPiece escaped_text,
+                                  size_t index,
+                                  uint32_t* code_point_out,
+                                  std::string* unescaped_out) {
+  GURL_DCHECK(unescaped_out->empty());
+
+  unsigned char bytes[CBU8_MAX_LENGTH];
+  if (!UnescapeUnsignedByteAtIndex(escaped_text, index, &bytes[0]))
+    return false;
+
+  size_t num_bytes = 1;
+
+  // If this is a lead byte, need to collect trail bytes as well.
+  if (CBU8_IS_LEAD(bytes[0])) {
+    // Look for the last trail byte of the UTF-8 character.  Give up once
+    // reach max character length number of bytes, or hit an unescaped
+    // character. No need to check length of escaped_text, as
+    // UnescapeUnsignedByteAtIndex checks lengths.
+    while (num_bytes < size(bytes) &&
+           UnescapeUnsignedByteAtIndex(escaped_text, index + num_bytes * 3,
+                                       &bytes[num_bytes]) &&
+           CBU8_IS_TRAIL(bytes[num_bytes])) {
+      ++num_bytes;
+    }
+  }
+
+  int32_t char_index = 0;
+  // Check if the unicode "character" that was just unescaped is valid.
+  if (!ReadUnicodeCharacter(reinterpret_cast<char*>(bytes), num_bytes,
+                            &char_index, code_point_out)) {
+    return false;
+  }
+
+  // It's possible that a prefix of |bytes| forms a valid UTF-8 character,
+  // and the rest are not valid UTF-8, so need to update |num_bytes| based
+  // on the result of ReadUnicodeCharacter().
+  num_bytes = char_index + 1;
+  *unescaped_out = std::string(reinterpret_cast<char*>(bytes), num_bytes);
+  return true;
+}
+
+// This method takes a Unicode code point and returns true if it should be
+// unescaped, based on |rules|.
+bool ShouldUnescapeCodePoint(UnescapeRule::Type rules, uint32_t code_point) {
+  // If this is an ASCII character, use the lookup table.
+  if (code_point < 0x80) {
+    return kUrlUnescape[code_point] ||
+           // Allow some additional unescaping when flags are set.
+           (code_point == ' ' && (rules & UnescapeRule::SPACES)) ||
+           // Allow any of the prohibited but non-control characters when doing
+           // "special" chars.
+           ((code_point == '/' || code_point == '\\') &&
+            (rules & UnescapeRule::PATH_SEPARATORS)) ||
+           (code_point > ' ' && code_point != '/' && code_point != '\\' &&
+            (rules & UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS));
+  }
+
+  // Compare the code point against a list of characters that can be used
+  // to spoof other URLs.
+  //
+  // Can't use icu to make this cleaner, because Cronet cannot depend on
+  // icu, and currently uses this file.
+  // TODO(https://crbug.com/829873): Try to make this use icu, both to
+  // protect against regressions as the Unicode standard is updated and to
+  // reduce the number of long lists of characters.
+  return !(
+      // Per http://tools.ietf.org/html/rfc3987#section-4.1, certain BiDi
+      // control characters are not allowed to appear unescaped in URLs.
+      code_point == 0x200E ||  // LEFT-TO-RIGHT MARK         (%E2%80%8E)
+      code_point == 0x200F ||  // RIGHT-TO-LEFT MARK         (%E2%80%8F)
+      code_point == 0x202A ||  // LEFT-TO-RIGHT EMBEDDING    (%E2%80%AA)
+      code_point == 0x202B ||  // RIGHT-TO-LEFT EMBEDDING    (%E2%80%AB)
+      code_point == 0x202C ||  // POP DIRECTIONAL FORMATTING (%E2%80%AC)
+      code_point == 0x202D ||  // LEFT-TO-RIGHT OVERRIDE     (%E2%80%AD)
+      code_point == 0x202E ||  // RIGHT-TO-LEFT OVERRIDE     (%E2%80%AE)
+
+      // The Unicode Technical Report (TR9) as referenced by RFC 3987 above has
+      // since added some new BiDi control characters that are not safe to
+      // unescape. http://www.unicode.org/reports/tr9
+      code_point == 0x061C ||  // ARABIC LETTER MARK         (%D8%9C)
+      code_point == 0x2066 ||  // LEFT-TO-RIGHT ISOLATE      (%E2%81%A6)
+      code_point == 0x2067 ||  // RIGHT-TO-LEFT ISOLATE      (%E2%81%A7)
+      code_point == 0x2068 ||  // FIRST STRONG ISOLATE       (%E2%81%A8)
+      code_point == 0x2069 ||  // POP DIRECTIONAL ISOLATE    (%E2%81%A9)
+
+      // The following spoofable characters are also banned in unescaped URLs,
+      // because they could be used to imitate parts of a web browser's UI.
+      code_point == 0x1F50F ||  // LOCK WITH INK PEN    (%F0%9F%94%8F)
+      code_point == 0x1F510 ||  // CLOSED LOCK WITH KEY (%F0%9F%94%90)
+      code_point == 0x1F512 ||  // LOCK                 (%F0%9F%94%92)
+      code_point == 0x1F513 ||  // OPEN LOCK            (%F0%9F%94%93)
+
+      // Spaces are also banned, as they can be used to scroll text out of view.
+      code_point == 0x0085 ||  // NEXT LINE                  (%C2%85)
+      code_point == 0x00A0 ||  // NO-BREAK SPACE             (%C2%A0)
+      code_point == 0x1680 ||  // OGHAM SPACE MARK           (%E1%9A%80)
+      code_point == 0x2000 ||  // EN QUAD                    (%E2%80%80)
+      code_point == 0x2001 ||  // EM QUAD                    (%E2%80%81)
+      code_point == 0x2002 ||  // EN SPACE                   (%E2%80%82)
+      code_point == 0x2003 ||  // EM SPACE                   (%E2%80%83)
+      code_point == 0x2004 ||  // THREE-PER-EM SPACE         (%E2%80%84)
+      code_point == 0x2005 ||  // FOUR-PER-EM SPACE          (%E2%80%85)
+      code_point == 0x2006 ||  // SIX-PER-EM SPACE           (%E2%80%86)
+      code_point == 0x2007 ||  // FIGURE SPACE               (%E2%80%87)
+      code_point == 0x2008 ||  // PUNCTUATION SPACE          (%E2%80%88)
+      code_point == 0x2009 ||  // THIN SPACE                 (%E2%80%89)
+      code_point == 0x200A ||  // HAIR SPACE                 (%E2%80%8A)
+      code_point == 0x2028 ||  // LINE SEPARATOR             (%E2%80%A8)
+      code_point == 0x2029 ||  // PARAGRAPH SEPARATOR        (%E2%80%A9)
+      code_point == 0x202F ||  // NARROW NO-BREAK SPACE      (%E2%80%AF)
+      code_point == 0x205F ||  // MEDIUM MATHEMATICAL SPACE  (%E2%81%9F)
+      code_point == 0x3000 ||  // IDEOGRAPHIC SPACE          (%E3%80%80)
+      // U+2800 is rendered as a space, but is not considered whitespace (see
+      // crbug.com/1068531).
+      code_point == 0x2800 ||  // BRAILLE PATTERN BLANK      (%E2%A0%80)
+
+      // Default Ignorable ([:Default_Ignorable_Code_Point=Yes:]) and Format
+      // characters ([:Cf:]) are also banned (see crbug.com/824715).
+      code_point == 0x00AD ||  // SOFT HYPHEN               (%C2%AD)
+      code_point == 0x034F ||  // COMBINING GRAPHEME JOINER (%CD%8F)
+      // Arabic number formatting
+      (code_point >= 0x0600 && code_point <= 0x0605) ||
+      // U+061C is already banned as a BiDi control character.
+      code_point == 0x06DD ||  // ARABIC END OF AYAH          (%DB%9D)
+      code_point == 0x070F ||  // SYRIAC ABBREVIATION MARK    (%DC%8F)
+      code_point == 0x08E2 ||  // ARABIC DISPUTED END OF AYAH (%E0%A3%A2)
+      code_point == 0x115F ||  // HANGUL CHOSEONG FILLER      (%E1%85%9F)
+      code_point == 0x1160 ||  // HANGUL JUNGSEONG FILLER     (%E1%85%A0)
+      code_point == 0x17B4 ||  // KHMER VOWEL INHERENT AQ     (%E1%9E%B4)
+      code_point == 0x17B5 ||  // KHMER VOWEL INHERENT AA     (%E1%9E%B5)
+      code_point == 0x180B ||  // MONGOLIAN FREE VARIATION SELECTOR ONE
+                               // (%E1%A0%8B)
+      code_point == 0x180C ||  // MONGOLIAN FREE VARIATION SELECTOR TWO
+                               // (%E1%A0%8C)
+      code_point == 0x180D ||  // MONGOLIAN FREE VARIATION SELECTOR THREE
+                               // (%E1%A0%8D)
+      code_point == 0x180E ||  // MONGOLIAN VOWEL SEPARATOR   (%E1%A0%8E)
+      code_point == 0x200B ||  // ZERO WIDTH SPACE            (%E2%80%8B)
+      code_point == 0x200C ||  // ZERO WIDTH SPACE NON-JOINER (%E2%80%8C)
+      code_point == 0x200D ||  // ZERO WIDTH JOINER           (%E2%80%8D)
+      // U+200E, U+200F, U+202A--202E, and U+2066--2069 are already banned as
+      // BiDi control characters.
+      code_point == 0x2060 ||  // WORD JOINER          (%E2%81%A0)
+      code_point == 0x2061 ||  // FUNCTION APPLICATION (%E2%81%A1)
+      code_point == 0x2062 ||  // INVISIBLE TIMES      (%E2%81%A2)
+      code_point == 0x2063 ||  // INVISIBLE SEPARATOR  (%E2%81%A3)
+      code_point == 0x2064 ||  // INVISIBLE PLUS       (%E2%81%A4)
+      code_point == 0x2065 ||  // null (%E2%81%A5)
+      // 0x2066--0x2069 are already banned as a BiDi control characters.
+      // General Punctuation - Deprecated (U+206A--206F)
+      (code_point >= 0x206A && code_point <= 0x206F) ||
+      code_point == 0x3164 ||  // HANGUL FILLER (%E3%85%A4)
+      (code_point >= 0xFFF0 && code_point <= 0xFFF8) ||  // null
+      // Variation selectors (%EF%B8%80 -- %EF%B8%8F)
+      (code_point >= 0xFE00 && code_point <= 0xFE0F) ||
+      code_point == 0xFEFF ||   // ZERO WIDTH NO-BREAK SPACE (%EF%BB%BF)
+      code_point == 0xFFA0 ||   // HALFWIDTH HANGUL FILLER (%EF%BE%A0)
+      code_point == 0xFFF9 ||   // INTERLINEAR ANNOTATION ANCHOR     (%EF%BF%B9)
+      code_point == 0xFFFA ||   // INTERLINEAR ANNOTATION SEPARATOR  (%EF%BF%BA)
+      code_point == 0xFFFB ||   // INTERLINEAR ANNOTATION TERMINATOR (%EF%BF%BB)
+      code_point == 0x110BD ||  // KAITHI NUMBER SIGN       (%F0%91%82%BD)
+      code_point == 0x110CD ||  // KAITHI NUMBER SIGN ABOVE (%F0%91%83%8D)
+      // Egyptian hieroglyph formatting (%F0%93%90%B0 -- %F0%93%90%B8)
+      (code_point >= 0x13430 && code_point <= 0x13438) ||
+      // Shorthand format controls (%F0%9B%B2%A0 -- %F0%9B%B2%A3)
+      (code_point >= 0x1BCA0 && code_point <= 0x1BCA3) ||
+      // Beams and slurs (%F0%9D%85%B3 -- %F0%9D%85%BA)
+      (code_point >= 0x1D173 && code_point <= 0x1D17A) ||
+      // Tags, Variation Selectors, nulls
+      (code_point >= 0xE0000 && code_point <= 0xE0FFF));
+}
+
+// Unescapes |escaped_text| according to |rules|, returning the resulting
+// string.  Fills in an |adjustments| parameter, if non-nullptr, so it reflects
+// the alterations done to the string that are not one-character-to-one-
+// character.  The resulting |adjustments| will always be sorted by increasing
+// offset.
+std::string UnescapeURLWithAdjustmentsImpl(
+    StringPiece escaped_text,
+    UnescapeRule::Type rules,
+    OffsetAdjuster::Adjustments* adjustments) {
+  if (adjustments)
+    adjustments->clear();
+  // Do not unescape anything, return the |escaped_text| text.
+  if (rules == UnescapeRule::NONE)
+    return escaped_text.as_string();
+
+  // The output of the unescaping is always smaller than the input, so we can
+  // reserve the input size to make sure we have enough buffer and don't have
+  // to allocate in the loop below.
+  std::string result;
+  result.reserve(escaped_text.length());
+
+  // Locations of adjusted text.
+  for (size_t i = 0, max = escaped_text.size(); i < max;) {
+    // Try to unescape the character.
+    uint32_t code_point;
+    std::string unescaped;
+    if (!UnescapeUTF8CharacterAtIndex(escaped_text, i, &code_point,
+                                      &unescaped)) {
+      // Check if the next character can be unescaped, but not as a valid UTF-8
+      // character. In that case, just unescaped and write the non-sense
+      // character.
+      //
+      // TODO(https://crbug.com/829868): Do not unescape illegal UTF-8
+      // sequences.
+      unsigned char non_utf8_byte;
+      if (UnescapeUnsignedByteAtIndex(escaped_text, i, &non_utf8_byte)) {
+        result.push_back(non_utf8_byte);
+        if (adjustments)
+          adjustments->push_back(OffsetAdjuster::Adjustment(i, 3, 1));
+        i += 3;
+        continue;
+      }
+
+      // Character is not escaped, so append as is, unless it's a '+' and
+      // REPLACE_PLUS_WITH_SPACE is being applied.
+      if (escaped_text[i] == '+' &&
+          (rules & UnescapeRule::REPLACE_PLUS_WITH_SPACE)) {
+        result.push_back(' ');
+      } else {
+        result.push_back(escaped_text[i]);
+      }
+      ++i;
+      continue;
+    }
+
+    GURL_DCHECK(!unescaped.empty());
+
+    if (!ShouldUnescapeCodePoint(rules, code_point)) {
+      // If it's a valid UTF-8 character, but not safe to unescape, copy all
+      // bytes directly.
+      result.append(escaped_text.begin() + i,
+                    escaped_text.begin() + i + 3 * unescaped.length());
+      i += unescaped.length() * 3;
+      continue;
+    }
+
+    // If the code point is allowed, and append the entire unescaped character.
+    result.append(unescaped);
+    if (adjustments) {
+      for (size_t j = 0; j < unescaped.length(); ++j) {
+        adjustments->push_back(OffsetAdjuster::Adjustment(i + j * 3, 3, 1));
+      }
+    }
+    i += 3 * unescaped.length();
+  }
+
+  return result;
+}
+
+}  // namespace
+
+std::string UnescapeURLComponent(StringPiece escaped_text,
+                                 UnescapeRule::Type rules) {
+  return UnescapeURLWithAdjustmentsImpl(escaped_text, rules, nullptr);
+}
+
+string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+    StringPiece text,
+    UnescapeRule::Type rules,
+    OffsetAdjuster::Adjustments* adjustments) {
+  string16 result;
+  OffsetAdjuster::Adjustments unescape_adjustments;
+  std::string unescaped_url(
+      UnescapeURLWithAdjustmentsImpl(text, rules, &unescape_adjustments));
+  if (UTF8ToUTF16WithAdjustments(unescaped_url.data(), unescaped_url.length(),
+                                 &result, adjustments)) {
+    // Character set looks like it's valid.
+    if (adjustments) {
+      OffsetAdjuster::MergeSequentialAdjustments(unescape_adjustments,
+                                                 adjustments);
+    }
+    return result;
+  }
+  // Character set is not valid.  Return the escaped version.
+  return UTF8ToUTF16WithAdjustments(text, adjustments);
+}
+
+std::string UnescapeBinaryURLComponent(StringPiece escaped_text,
+                                       UnescapeRule::Type rules) {
+  // Only NORMAL and REPLACE_PLUS_WITH_SPACE are supported.
+  GURL_DCHECK(rules != UnescapeRule::NONE);
+  GURL_DCHECK(!(rules &
+           ~(UnescapeRule::NORMAL | UnescapeRule::REPLACE_PLUS_WITH_SPACE)));
+
+  std::string unescaped_text;
+
+  // The output of the unescaping is always smaller than the input, so we can
+  // reserve the input size to make sure we have enough buffer and don't have
+  // to allocate in the loop below.
+  // Increase capacity before size, as just resizing can grow capacity
+  // needlessly beyond our requested size.
+  unescaped_text.reserve(escaped_text.size());
+  unescaped_text.resize(escaped_text.size());
+
+  size_t output_index = 0;
+
+  for (size_t i = 0, max = escaped_text.size(); i < max;) {
+    unsigned char byte;
+    // UnescapeUnsignedByteAtIndex does bounds checking, so this is always safe
+    // to call.
+    if (UnescapeUnsignedByteAtIndex(escaped_text, i, &byte)) {
+      unescaped_text[output_index++] = byte;
+      i += 3;
+      continue;
+    }
+
+    if ((rules & UnescapeRule::REPLACE_PLUS_WITH_SPACE) &&
+        escaped_text[i] == '+') {
+      unescaped_text[output_index++] = ' ';
+      ++i;
+      continue;
+    }
+
+    unescaped_text[output_index++] = escaped_text[i++];
+  }
+
+  GURL_DCHECK_LE(output_index, unescaped_text.size());
+  unescaped_text.resize(output_index);
+  return unescaped_text;
+}
+
+bool UnescapeBinaryURLComponentSafe(StringPiece escaped_text,
+                                    bool fail_on_path_separators,
+                                    std::string* unescaped_text) {
+  unescaped_text->clear();
+
+  std::set<unsigned char> illegal_encoded_bytes;
+  for (char c = '\x00'; c < '\x20'; ++c) {
+    illegal_encoded_bytes.insert(c);
+  }
+  if (fail_on_path_separators) {
+    illegal_encoded_bytes.insert('/');
+    illegal_encoded_bytes.insert('\\');
+  }
+  if (ContainsEncodedBytes(escaped_text, illegal_encoded_bytes))
+    return false;
+
+  *unescaped_text = UnescapeBinaryURLComponent(escaped_text);
+  return true;
+}
+
+bool ContainsEncodedBytes(StringPiece escaped_text,
+                          const std::set<unsigned char>& bytes) {
+  for (size_t i = 0, max = escaped_text.size(); i < max;) {
+    unsigned char byte;
+    // UnescapeUnsignedByteAtIndex does bounds checking, so this is always safe
+    // to call.
+    if (UnescapeUnsignedByteAtIndex(escaped_text, i, &byte)) {
+      if (bytes.find(byte) != bytes.end())
+        return true;
+
+      i += 3;
+      continue;
+    }
+
+    ++i;
+  }
+
+  return false;
+}
+
+}  // namespace base
\ No newline at end of file
diff --git a/base/strings/escape.h b/base/strings/escape.h
new file mode 100644
index 0000000..0bb6aea
--- /dev/null
+++ b/base/strings/escape.h
@@ -0,0 +1,121 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_ESCAPE_H_
+#define BASE_STRINGS_ESCAPE_H_
+
+#include <stdint.h>
+
+#include <set>
+#include <string>
+
+#include "polyfills/base/base_export.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_offset_string_conversions.h"
+
+namespace gurl_base {
+
+class UnescapeRule {
+ public:
+  // A combination of the following flags that is passed to the unescaping
+  // functions.
+  typedef uint32_t Type;
+
+  enum {
+    // Don't unescape anything at all.
+    NONE = 0,
+
+    // Don't unescape anything special, but all normal unescaping will happen.
+    // This is a placeholder and can't be combined with other flags (since it's
+    // just the absence of them). All other unescape rules imply "normal" in
+    // addition to their special meaning. Things like escaped letters, digits,
+    // and most symbols will get unescaped with this mode.
+    NORMAL = 1 << 0,
+
+    // Convert %20 to spaces. In some places where we're showing URLs, we may
+    // want this. In places where the URL may be copied and pasted out, then
+    // you wouldn't want this since it might not be interpreted in one piece
+    // by other applications.  Other UTF-8 spaces will not be unescaped.
+    SPACES = 1 << 1,
+
+    // Unescapes '/' and '\\'. If these characters were unescaped, the resulting
+    // URL won't be the same as the source one. Moreover, they are dangerous to
+    // unescape in strings that will be used as file paths or names. This value
+    // should only be used when slashes don't have special meaning, like data
+    // URLs.
+    PATH_SEPARATORS = 1 << 2,
+
+    // Unescapes various characters that will change the meaning of URLs,
+    // including '%', '+', '&', '#'. Does not unescape path separators.
+    // If these characters were unescaped, the resulting URL won't be the same
+    // as the source one. This flag is used when generating final output like
+    // filenames for URLs where we won't be interpreting as a URL and want to do
+    // as much unescaping as possible.
+    URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS = 1 << 3,
+
+    // URL queries use "+" for space. This flag controls that replacement.
+    REPLACE_PLUS_WITH_SPACE = 1 << 4,
+  };
+};
+
+// Unescapes |escaped_text| and returns the result.
+// Unescaping consists of looking for the exact pattern "%XX", where each X is
+// a hex digit, and converting to the character with the numerical value of
+// those digits. Thus "i%20=%203%3b" unescapes to "i = 3;", if the
+// "UnescapeRule::SPACES" used.
+//
+// This method does not ensure that the output is a valid string using any
+// character encoding. However, it does leave escaped certain byte sequences
+// that would be dangerous to display to the user, because if interpreted as
+// UTF-8, they could be used to mislead the user. Callers that want to
+// unconditionally unescape everything for uses other than displaying data to
+// the user should use UnescapeBinaryURLComponent().
+BASE_EXPORT std::string UnescapeURLComponent(StringPiece escaped_text,
+                                             UnescapeRule::Type rules);
+
+// Unescapes the given substring as a URL, and then tries to interpret the
+// result as being encoded as UTF-8. If the result is convertible into UTF-8, it
+// will be returned as converted. If it is not, the original escaped string will
+// be converted into a string16 and returned.  |adjustments| provides
+// information on how the original string was adjusted to get the string
+// returned.
+BASE_EXPORT string16 UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+    StringPiece text,
+    UnescapeRule::Type rules,
+    OffsetAdjuster::Adjustments* adjustments);
+
+// Unescapes a component of a URL for use as binary data. Unlike
+// UnescapeURLComponent, leaves nothing unescaped, including nulls, invalid
+// characters, characters that are unsafe to display, etc. This should *not*
+// be used when displaying the decoded data to the user.
+//
+// Only the NORMAL and REPLACE_PLUS_WITH_SPACE rules are allowed.
+BASE_EXPORT std::string UnescapeBinaryURLComponent(
+    StringPiece escaped_text,
+    UnescapeRule::Type rules = UnescapeRule::NORMAL);
+
+// Variant of UnescapeBinaryURLComponent().  Writes output to |unescaped_text|.
+// Returns true on success, returns false and clears |unescaped_text| on
+// failure. Fails on characters escaped that are unsafe to unescape in some
+// contexts, which are defined as characters "\0" through "\x1F" (Which includes
+// CRLF but not space), and optionally path separators. Path separators include
+// both forward and backward slashes on all platforms. Does not fail if any of
+// those characters appear unescaped in the input string.
+BASE_EXPORT bool UnescapeBinaryURLComponentSafe(StringPiece escaped_text,
+                                                bool fail_on_path_separators,
+                                                std::string* unescaped_text);
+
+// Returns true if |escaped_text| contains any element of |bytes| in
+// percent-encoded form.
+//
+// For example, if |bytes| is {'%', '/'}, returns true if |escaped_text|
+// contains "%25" or "%2F", but not if it just contains bare '%' or '/'
+// characters.
+BASE_EXPORT bool ContainsEncodedBytes(StringPiece escaped_text,
+                                      const std::set<unsigned char>& bytes);
+
+}  // namespace base
+
+#endif  // BASE_STRINGS_ESCAPE_H_
diff --git a/base/strings/escape_unittest.cc b/base/strings/escape_unittest.cc
new file mode 100644
index 0000000..e6c0b1a
--- /dev/null
+++ b/base/strings/escape_unittest.cc
@@ -0,0 +1,429 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <string>
+
+#include "base/strings/escape.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gurl_base {
+
+struct UnescapeURLCase {
+  const char* input;
+  UnescapeRule::Type rules;
+  const char* output;
+};
+
+struct UnescapeAndDecodeCase {
+  const char* input;
+
+  // The expected output when run through UnescapeURL.
+  const char* url_unescaped;
+
+  // The expected output when run through UnescapeQuery.
+  const char* query_unescaped;
+
+  // The expected output when run through UnescapeAndDecodeURLComponent.
+  const wchar_t* decoded;
+};
+
+struct AdjustOffsetCase {
+  const char* input;
+  size_t input_offset;
+  size_t output_offset;
+};
+
+TEST(EscapeTest, DataURLWithAccentedCharacters) {
+  const std::string url =
+      "text/html;charset=utf-8,%3Chtml%3E%3Cbody%3ETonton,%20ton%20th%C3"
+      "%A9%20t'a-t-il%20%C3%B4t%C3%A9%20ta%20toux%20";
+
+  OffsetAdjuster::Adjustments adjustments;
+  UnescapeAndDecodeUTF8URLComponentWithAdjustments(url, UnescapeRule::SPACES,
+                                                   &adjustments);
+}
+
+TEST(EscapeTest, UnescapeURLComponent) {
+  const UnescapeURLCase kUnescapeCases[] = {
+      {"", UnescapeRule::NORMAL, ""},
+      {"%2", UnescapeRule::NORMAL, "%2"},
+      {"%%%%%%", UnescapeRule::NORMAL, "%%%%%%"},
+      {"Don't escape anything", UnescapeRule::NORMAL, "Don't escape anything"},
+      {"Invalid %escape %2", UnescapeRule::NORMAL, "Invalid %escape %2"},
+      {"Some%20random text %25%2dOK", UnescapeRule::NONE,
+       "Some%20random text %25%2dOK"},
+      {"Some%20random text %25%2dOK", UnescapeRule::NORMAL,
+       "Some%20random text %25-OK"},
+      {"Some%20random text %25%E1%A6", UnescapeRule::NORMAL,
+       "Some%20random text %25\xE1\xA6"},
+      {"Some%20random text %25%E1%A6OK", UnescapeRule::NORMAL,
+       "Some%20random text %25\xE1\xA6OK"},
+      {"Some%20random text %25%E1%A6%99OK", UnescapeRule::NORMAL,
+       "Some%20random text %25\xE1\xA6\x99OK"},
+
+      // BiDi Control characters should not be unescaped.
+      {"Some%20random text %25%D8%9COK", UnescapeRule::NORMAL,
+       "Some%20random text %25%D8%9COK"},
+      {"Some%20random text %25%E2%80%8EOK", UnescapeRule::NORMAL,
+       "Some%20random text %25%E2%80%8EOK"},
+      {"Some%20random text %25%E2%80%8FOK", UnescapeRule::NORMAL,
+       "Some%20random text %25%E2%80%8FOK"},
+      {"Some%20random text %25%E2%80%AAOK", UnescapeRule::NORMAL,
+       "Some%20random text %25%E2%80%AAOK"},
+      {"Some%20random text %25%E2%80%ABOK", UnescapeRule::NORMAL,
+       "Some%20random text %25%E2%80%ABOK"},
+      {"Some%20random text %25%E2%80%AEOK", UnescapeRule::NORMAL,
+       "Some%20random text %25%E2%80%AEOK"},
+      {"Some%20random text %25%E2%81%A6OK", UnescapeRule::NORMAL,
+       "Some%20random text %25%E2%81%A6OK"},
+      {"Some%20random text %25%E2%81%A9OK", UnescapeRule::NORMAL,
+       "Some%20random text %25%E2%81%A9OK"},
+
+      // Certain banned characters should not be unescaped.
+      // U+1F50F LOCK WITH INK PEN
+      {"Some%20random text %25%F0%9F%94%8FOK", UnescapeRule::NORMAL,
+       "Some%20random text %25%F0%9F%94%8FOK"},
+      // U+1F510 CLOSED LOCK WITH KEY
+      {"Some%20random text %25%F0%9F%94%90OK", UnescapeRule::NORMAL,
+       "Some%20random text %25%F0%9F%94%90OK"},
+      // U+1F512 LOCK
+      {"Some%20random text %25%F0%9F%94%92OK", UnescapeRule::NORMAL,
+       "Some%20random text %25%F0%9F%94%92OK"},
+      // U+1F513 OPEN LOCK
+      {"Some%20random text %25%F0%9F%94%93OK", UnescapeRule::NORMAL,
+       "Some%20random text %25%F0%9F%94%93OK"},
+
+      // Spaces
+      {"(%C2%85)(%C2%A0)(%E1%9A%80)(%E2%80%80)", UnescapeRule::NORMAL,
+       "(%C2%85)(%C2%A0)(%E1%9A%80)(%E2%80%80)"},
+      {"(%E2%80%81)(%E2%80%82)(%E2%80%83)(%E2%80%84)", UnescapeRule::NORMAL,
+       "(%E2%80%81)(%E2%80%82)(%E2%80%83)(%E2%80%84)"},
+      {"(%E2%80%85)(%E2%80%86)(%E2%80%87)(%E2%80%88)", UnescapeRule::NORMAL,
+       "(%E2%80%85)(%E2%80%86)(%E2%80%87)(%E2%80%88)"},
+      {"(%E2%80%89)(%E2%80%8A)(%E2%80%A8)(%E2%80%A9)", UnescapeRule::NORMAL,
+       "(%E2%80%89)(%E2%80%8A)(%E2%80%A8)(%E2%80%A9)"},
+      {"(%E2%80%AF)(%E2%81%9F)(%E3%80%80)", UnescapeRule::NORMAL,
+       "(%E2%80%AF)(%E2%81%9F)(%E3%80%80)"},
+      {"(%E2%A0%80)", UnescapeRule::NORMAL, "(%E2%A0%80)"},
+
+      // Default Ignorable and Formatting characters should not be unescaped.
+      {"(%E2%81%A5)(%EF%BF%B0)(%EF%BF%B8)", UnescapeRule::NORMAL,
+       "(%E2%81%A5)(%EF%BF%B0)(%EF%BF%B8)"},
+      {"(%F3%A0%82%80)(%F3%A0%83%BF)(%F3%A0%87%B0)", UnescapeRule::NORMAL,
+       "(%F3%A0%82%80)(%F3%A0%83%BF)(%F3%A0%87%B0)"},
+      {"(%F3%A0%BF%BF)(%C2%AD)(%CD%8F)", UnescapeRule::NORMAL,
+       "(%F3%A0%BF%BF)(%C2%AD)(%CD%8F)"},
+      {"(%D8%80%20)(%D8%85)(%DB%9D)(%DC%8F)(%E0%A3%A2)", UnescapeRule::NORMAL,
+       "(%D8%80%20)(%D8%85)(%DB%9D)(%DC%8F)(%E0%A3%A2)"},
+      {"(%E1%85%9F)(%E1%85%A0)(%E1%9E%B4)(%E1%9E%B5)", UnescapeRule::NORMAL,
+       "(%E1%85%9F)(%E1%85%A0)(%E1%9E%B4)(%E1%9E%B5)"},
+      {"(%E1%A0%8B)(%E1%A0%8C)(%E1%A0%8D)(%E1%A0%8E)", UnescapeRule::NORMAL,
+       "(%E1%A0%8B)(%E1%A0%8C)(%E1%A0%8D)(%E1%A0%8E)"},
+      {"(%E2%80%8B)(%E2%80%8C)(%E2%80%8D)(%E2%81%A0)", UnescapeRule::NORMAL,
+       "(%E2%80%8B)(%E2%80%8C)(%E2%80%8D)(%E2%81%A0)"},
+      {"(%E2%81%A1)(%E2%81%A2)(%E2%81%A3)(%E2%81%A4)", UnescapeRule::NORMAL,
+       "(%E2%81%A1)(%E2%81%A2)(%E2%81%A3)(%E2%81%A4)"},
+      {"(%E3%85%A4)(%EF%BB%BF)(%EF%BE%A0)(%EF%BF%B9)", UnescapeRule::NORMAL,
+       "(%E3%85%A4)(%EF%BB%BF)(%EF%BE%A0)(%EF%BF%B9)"},
+      {"(%EF%BF%BB)(%F0%91%82%BD)(%F0%91%83%8D)", UnescapeRule::NORMAL,
+       "(%EF%BF%BB)(%F0%91%82%BD)(%F0%91%83%8D)"},
+      {"(%F0%93%90%B0)(%F0%93%90%B8)", UnescapeRule::NORMAL,
+       "(%F0%93%90%B0)(%F0%93%90%B8)"},
+      // General Punctuation - Deprecated (U+206A--206F)
+      {"(%E2%81%AA)(%E2%81%AD)(%E2%81%AF)", UnescapeRule::NORMAL,
+       "(%E2%81%AA)(%E2%81%AD)(%E2%81%AF)"},
+      // Variation selectors (U+FE00--FE0F)
+      {"(%EF%B8%80)(%EF%B8%8C)(%EF%B8%8D)", UnescapeRule::NORMAL,
+       "(%EF%B8%80)(%EF%B8%8C)(%EF%B8%8D)"},
+      // Shorthand format controls (U+1BCA0--1BCA3)
+      {"(%F0%9B%B2%A0)(%F0%9B%B2%A1)(%F0%9B%B2%A3)", UnescapeRule::NORMAL,
+       "(%F0%9B%B2%A0)(%F0%9B%B2%A1)(%F0%9B%B2%A3)"},
+      // Musical symbols beams and slurs (U+1D173--1D17A)
+      {"(%F0%9D%85%B3)(%F0%9D%85%B9)(%F0%9D%85%BA)", UnescapeRule::NORMAL,
+       "(%F0%9D%85%B3)(%F0%9D%85%B9)(%F0%9D%85%BA)"},
+      // Tags block (U+E0000--E007F), includes unassigned points
+      {"(%F3%A0%80%80)(%F3%A0%80%81)(%F3%A0%81%8F)", UnescapeRule::NORMAL,
+       "(%F3%A0%80%80)(%F3%A0%80%81)(%F3%A0%81%8F)"},
+      // Ideographic-specific variation selectors (U+E0100--E01EF)
+      {"(%F3%A0%84%80)(%F3%A0%84%90)(%F3%A0%87%AF)", UnescapeRule::NORMAL,
+       "(%F3%A0%84%80)(%F3%A0%84%90)(%F3%A0%87%AF)"},
+
+      // Two spoofing characters in a row should not be unescaped.
+      {"%D8%9C%D8%9C", UnescapeRule::NORMAL, "%D8%9C%D8%9C"},
+      // Non-spoofing characters surrounded by spoofing characters should be
+      // unescaped.
+      {"%D8%9C%C2%A1%D8%9C%C2%A1", UnescapeRule::NORMAL,
+       "%D8%9C\xC2\xA1%D8%9C\xC2\xA1"},
+      // Invalid UTF-8 characters surrounded by spoofing characters should be
+      // unescaped.
+      {"%D8%9C%85%D8%9C%85", UnescapeRule::NORMAL, "%D8%9C\x85%D8%9C\x85"},
+      // Test with enough trail bytes to overflow the CBU8_MAX_LENGTH-byte
+      // buffer. The first two bytes are a spoofing character as well.
+      {"%D8%9C%9C%9C%9C%9C%9C%9C%9C%9C%9C", UnescapeRule::NORMAL,
+       "%D8%9C\x9C\x9C\x9C\x9C\x9C\x9C\x9C\x9C\x9C"},
+
+      {"Some%20random text %25%2dOK", UnescapeRule::SPACES,
+       "Some random text %25-OK"},
+      {"Some%20random text %25%2dOK", UnescapeRule::PATH_SEPARATORS,
+       "Some%20random text %25-OK"},
+      {"Some%20random text %25%2dOK",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "Some%20random text %-OK"},
+      {"Some%20random text %25%2dOK",
+       UnescapeRule::SPACES |
+           UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "Some random text %-OK"},
+      {"%A0%B1%C2%D3%E4%F5", UnescapeRule::NORMAL, "\xA0\xB1\xC2\xD3\xE4\xF5"},
+      {"%Aa%Bb%Cc%Dd%Ee%Ff", UnescapeRule::NORMAL, "\xAa\xBb\xCc\xDd\xEe\xFf"},
+      // Certain URL-sensitive characters should not be unescaped unless asked.
+      {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
+       UnescapeRule::SPACES, "Hello %13%10world %23# %3F? %3D= %26& %25% %2B+"},
+      {"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "Hello%20%13%10world ## ?? == && %% ++"},
+      // We can neither escape nor unescape '@' since some websites expect it to
+      // be preserved as either '@' or "%40".
+      // See http://b/996720 and http://crbug.com/23933 .
+      {"me@my%40example", UnescapeRule::NORMAL, "me@my%40example"},
+      // Control characters.
+      {"%01%02%03%04%05%06%07%08%09 %25",
+       UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS,
+       "%01%02%03%04%05%06%07%08%09 %"},
+      {"Hello%20%13%10%02", UnescapeRule::SPACES, "Hello %13%10%02"},
+
+      // '/' and '\\' should only be unescaped by PATH_SEPARATORS.
+      {"%2F%5C", UnescapeRule::PATH_SEPARATORS, "/\\"},
+  };
+
+  for (const auto unescape_case : kUnescapeCases) {
+    EXPECT_EQ(unescape_case.output,
+              UnescapeURLComponent(unescape_case.input, unescape_case.rules));
+  }
+
+  // Test NULL character unescaping, which can't be tested above since those are
+  // just char pointers.
+  std::string input("Null");
+  input.push_back(0);  // Also have a NULL in the input.
+  input.append("%00%39Test");
+
+  std::string expected = "Null";
+  expected.push_back(0);
+  expected.append("%009Test");
+  EXPECT_EQ(expected, UnescapeURLComponent(input, UnescapeRule::NORMAL));
+}
+
+TEST(EscapeTest, UnescapeAndDecodeUTF8URLComponentWithAdjustments) {
+  const UnescapeAndDecodeCase unescape_cases[] = {
+      {"%", "%", "%", L"%"},
+      {"+", "+", " ", L"+"},
+      {"%2+", "%2+", "%2 ", L"%2+"},
+      {"+%%%+%%%", "+%%%+%%%", " %%% %%%", L"+%%%+%%%"},
+      {"Don't escape anything", "Don't escape anything",
+       "Don't escape anything", L"Don't escape anything"},
+      {"+Invalid %escape %2+", "+Invalid %escape %2+", " Invalid %escape %2 ",
+       L"+Invalid %escape %2+"},
+      {"Some random text %25%2dOK", "Some random text %25-OK",
+       "Some random text %25-OK", L"Some random text %25-OK"},
+      {"%01%02%03%04%05%06%07%08%09", "%01%02%03%04%05%06%07%08%09",
+       "%01%02%03%04%05%06%07%08%09", L"%01%02%03%04%05%06%07%08%09"},
+      {"%E4%BD%A0+%E5%A5%BD", "\xE4\xBD\xA0+\xE5\xA5\xBD",
+       "\xE4\xBD\xA0 \xE5\xA5\xBD", L"\x4f60+\x597d"},
+      {"%ED%ED",                            // Invalid UTF-8.
+       "\xED\xED", "\xED\xED", L"%ED%ED"},  // Invalid UTF-8 -> kept unescaped.
+  };
+
+  for (const auto& unescape_case : unescape_cases) {
+    std::string unescaped =
+        UnescapeURLComponent(unescape_case.input, UnescapeRule::NORMAL);
+    EXPECT_EQ(std::string(unescape_case.url_unescaped), unescaped);
+
+    unescaped = UnescapeURLComponent(unescape_case.input,
+                                     UnescapeRule::REPLACE_PLUS_WITH_SPACE);
+    EXPECT_EQ(std::string(unescape_case.query_unescaped), unescaped);
+
+    // The adjustments argument is covered by the next test.
+    //
+    // TODO: Need to test unescape_spaces and unescape_percent.
+    string16 decoded = UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+        unescape_case.input, UnescapeRule::NORMAL, nullptr);
+    EXPECT_EQ(WideToUTF16(unescape_case.decoded), decoded);
+  }
+}
+
+TEST(EscapeTest, AdjustOffset) {
+  const AdjustOffsetCase adjust_cases[] = {
+      {"", 0, 0},
+      {"test", 0, 0},
+      {"test", 2, 2},
+      {"test", 4, 4},
+      {"test", std::string::npos, std::string::npos},
+      {"%2dtest", 6, 4},
+      {"%2dtest", 3, 1},
+      {"%2dtest", 2, std::string::npos},
+      {"%2dtest", 1, std::string::npos},
+      {"%2dtest", 0, 0},
+      {"test%2d", 2, 2},
+      {"test%2e", 2, 2},
+      {"%E4%BD%A0+%E5%A5%BD", 9, 1},
+      {"%E4%BD%A0+%E5%A5%BD", 6, std::string::npos},
+      {"%E4%BD%A0+%E5%A5%BD", 0, 0},
+      {"%E4%BD%A0+%E5%A5%BD", 10, 2},
+      {"%E4%BD%A0+%E5%A5%BD", 19, 3},
+
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 18, 8},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 15, std::string::npos},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 9, 7},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 19, 9},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 28, 10},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 0, 0},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 2, 2},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 3, std::string::npos},
+      {"hi%41test%E4%BD%A0+%E5%A5%BD", 5, 3},
+
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 9, 1},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 6, std::string::npos},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 0, 0},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 10, 2},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 19, 3},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 21, 5},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 22, std::string::npos},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 24, 6},
+      {"%E4%BD%A0+%E5%A5%BDhi%41test", 28, 10},
+
+      {"%ED%B0%80+%E5%A5%BD", 6, 6},  // not convertible to UTF-8
+  };
+
+  for (const auto& adjust_case : adjust_cases) {
+    size_t offset = adjust_case.input_offset;
+    OffsetAdjuster::Adjustments adjustments;
+    UnescapeAndDecodeUTF8URLComponentWithAdjustments(
+        adjust_case.input, UnescapeRule::NORMAL, &adjustments);
+    OffsetAdjuster::AdjustOffset(adjustments, &offset);
+    EXPECT_EQ(adjust_case.output_offset, offset)
+        << "input=" << adjust_case.input
+        << " offset=" << adjust_case.input_offset;
+  }
+}
+
+TEST(EscapeTest, UnescapeBinaryURLComponent) {
+  const UnescapeURLCase kTestCases[] = {
+      // Check that ASCII characters with special handling in
+      // UnescapeURLComponent() are still unescaped.
+      {"%09%20%25foo%2F", UnescapeRule::NORMAL, "\x09 %foo/"},
+
+      // UTF-8 Characters banned by UnescapeURLComponent() should also be
+      // unescaped.
+      {"Some random text %D8%9COK", UnescapeRule::NORMAL,
+       "Some random text \xD8\x9COK"},
+      {"Some random text %F0%9F%94%8FOK", UnescapeRule::NORMAL,
+       "Some random text \xF0\x9F\x94\x8FOK"},
+
+      // As should invalid UTF-8 characters.
+      {"%A0%A0%E9%E9%A0%A0%A0%A0", UnescapeRule::NORMAL,
+       "\xA0\xA0\xE9\xE9\xA0\xA0\xA0\xA0"},
+
+      // And valid UTF-8 characters that are not banned by
+      // UnescapeURLComponent() should be unescaped, too!
+      {"%C2%A1%C2%A1", UnescapeRule::NORMAL, "\xC2\xA1\xC2\xA1"},
+
+      // '+' should be left alone by default
+      {"++%2B++", UnescapeRule::NORMAL, "+++++"},
+      // But should magically be turned into a space if requested.
+      {"++%2B++", UnescapeRule::REPLACE_PLUS_WITH_SPACE, "  +  "},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    EXPECT_EQ(test_case.output,
+              UnescapeBinaryURLComponent(test_case.input, test_case.rules));
+  }
+
+  // Test NULL character unescaping, which can't be tested above since those are
+  // just char pointers.
+  std::string input("Null");
+  input.push_back(0);  // Also have a NULL in the input.
+  input.append("%00%39Test");
+
+  std::string expected("Null");
+  expected.push_back(0);
+  expected.push_back(0);
+  expected.append("9Test");
+  EXPECT_EQ(expected, UnescapeBinaryURLComponent(input));
+}
+
+TEST(EscapeTest, UnescapeBinaryURLComponentSafe) {
+  const struct TestCase {
+    const char* input;
+    // Expected output. Null if call is expected to fail when
+    // |fail_on_path_separators| is false.
+    const char* expected_output;
+    // Whether |input| has any escaped path separators.
+    bool has_path_separators;
+  } kTestCases[] = {
+      // Spaces, percents, and invalid UTF-8 characters are all successfully
+      // unescaped.
+      {"%20%25foo%81", " %foo\x81", false},
+
+      // Characters disallowed unconditionally.
+      {"foo%00", nullptr, false},
+      {"foo%01", nullptr, false},
+      {"foo%0A", nullptr, false},
+      {"foo%0D", nullptr, false},
+
+      // Path separators.
+      {"foo%2F", "foo/", true},
+      {"foo%5C", "foo\\", true},
+
+      // Characters that are considered invalid to escape are ignored if passed
+      // in unescaped.
+      {"foo\x01\r/\\", "foo\x01\r/\\", false},
+  };
+
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.input);
+
+    std::string output = "foo";
+    if (!test_case.expected_output) {
+      EXPECT_FALSE(UnescapeBinaryURLComponentSafe(
+          test_case.input, false /* fail_on_path_separators */, &output));
+      EXPECT_TRUE(output.empty());
+      EXPECT_FALSE(UnescapeBinaryURLComponentSafe(
+          test_case.input, true /* fail_on_path_separators */, &output));
+      EXPECT_TRUE(output.empty());
+      continue;
+    }
+    EXPECT_TRUE(UnescapeBinaryURLComponentSafe(
+        test_case.input, false /* fail_on_path_separators */, &output));
+    EXPECT_EQ(test_case.expected_output, output);
+    if (test_case.has_path_separators) {
+      EXPECT_FALSE(UnescapeBinaryURLComponentSafe(
+          test_case.input, true /* fail_on_path_separators */, &output));
+      EXPECT_TRUE(output.empty());
+    } else {
+      output = "foo";
+      EXPECT_TRUE(UnescapeBinaryURLComponentSafe(
+          test_case.input, true /* fail_on_path_separators */, &output));
+      EXPECT_EQ(test_case.expected_output, output);
+    }
+  }
+}
+
+TEST(EscapeTest, ContainsEncodedBytes) {
+  EXPECT_FALSE(ContainsEncodedBytes("abc/def", {'/', '\\'}));
+  EXPECT_FALSE(ContainsEncodedBytes("abc%2Fdef", {'%'}));
+  EXPECT_TRUE(ContainsEncodedBytes("abc%252Fdef", {'%'}));
+  EXPECT_TRUE(ContainsEncodedBytes("abc%2Fdef", {'/', '\\'}));
+  EXPECT_TRUE(ContainsEncodedBytes("abc%5Cdef", {'/', '\\'}));
+  EXPECT_TRUE(ContainsEncodedBytes("abc%2fdef", {'/', '\\'}));
+
+  // Should be looking for byte values, not UTF-8 character values.
+  EXPECT_TRUE(ContainsEncodedBytes("caf%C3%A9", {'\xc3'}));
+  EXPECT_FALSE(ContainsEncodedBytes("caf%C3%A9", {'\xe9'}));
+}
+
+}  // namespace base
diff --git a/base/strings/safe_sprintf.cc b/base/strings/safe_sprintf.cc
index 726ccf9..e8bb070 100644
--- a/base/strings/safe_sprintf.cc
+++ b/base/strings/safe_sprintf.cc
@@ -174,7 +174,7 @@
   // |count_| will also be incremented by the number of bytes that were meant
   // to be emitted. The |pad| character is typically either a ' ' space
   // or a '0' zero, but other non-NUL values are legal.
-  // Returns "false", iff the the |buffer_| filled up (i.e. |count_|
+  // Returns "false", iff the |buffer_| filled up (i.e. |count_|
   // overflowed |size_|) at any time during padding.
   inline bool Pad(char pad, size_t padding, size_t len) {
     DEBUG_CHECK(pad);
diff --git a/base/strings/string_number_conversions_unittest.cc b/base/strings/string_number_conversions_unittest.cc
index 6f8d171..62a31f6 100644
--- a/base/strings/string_number_conversions_unittest.cc
+++ b/base/strings/string_number_conversions_unittest.cc
@@ -44,7 +44,8 @@
       {0, "0", "0"},
       {-1, "-1", "18446744073709551615"},
       {
-          std::numeric_limits<int64_t>::max(), "9223372036854775807",
+          std::numeric_limits<int64_t>::max(),
+          "9223372036854775807",
           "9223372036854775807",
       },
       {std::numeric_limits<int64_t>::min(), "-9223372036854775808",
@@ -111,30 +112,32 @@
     int output;
     bool success;
   } cases[] = {
-    {"0", 0, true},
-    {"42", 42, true},
-    {"42\x99", 42, false},
-    {"\x99" "42\x99", 0, false},
-    {"-2147483648", INT_MIN, true},
-    {"2147483647", INT_MAX, true},
-    {"", 0, false},
-    {" 42", 42, false},
-    {"42 ", 42, false},
-    {"\t\n\v\f\r 42", 42, false},
-    {"blah42", 0, false},
-    {"42blah", 42, false},
-    {"blah42blah", 0, false},
-    {"-273.15", -273, false},
-    {"+98.6", 98, false},
-    {"--123", 0, false},
-    {"++123", 0, false},
-    {"-+123", 0, false},
-    {"+-123", 0, false},
-    {"-", 0, false},
-    {"-2147483649", INT_MIN, false},
-    {"-99999999999", INT_MIN, false},
-    {"2147483648", INT_MAX, false},
-    {"99999999999", INT_MAX, false},
+      {"0", 0, true},
+      {"42", 42, true},
+      {"42\x99", 42, false},
+      {"\x99"
+       "42\x99",
+       0, false},
+      {"-2147483648", INT_MIN, true},
+      {"2147483647", INT_MAX, true},
+      {"", 0, false},
+      {" 42", 42, false},
+      {"42 ", 42, false},
+      {"\t\n\v\f\r 42", 42, false},
+      {"blah42", 0, false},
+      {"42blah", 42, false},
+      {"blah42blah", 0, false},
+      {"-273.15", -273, false},
+      {"+98.6", 98, false},
+      {"--123", 0, false},
+      {"++123", 0, false},
+      {"-+123", 0, false},
+      {"+-123", 0, false},
+      {"-", 0, false},
+      {"-2147483649", INT_MIN, false},
+      {"-99999999999", INT_MIN, false},
+      {"2147483648", INT_MAX, false},
+      {"99999999999", INT_MAX, false},
   };
 
   for (const auto& i : cases) {
@@ -163,7 +166,7 @@
   EXPECT_EQ(6, output);
 
   output = 0;
-  const char16 negative_wide_input[] = { 0xFF4D, '4', '2', 0};
+  const char16 negative_wide_input[] = {0xFF4D, '4', '2', 0};
   EXPECT_FALSE(StringToInt(string16(negative_wide_input), &output));
   EXPECT_EQ(0, output);
 }
@@ -174,31 +177,33 @@
     unsigned output;
     bool success;
   } cases[] = {
-    {"0", 0, true},
-    {"42", 42, true},
-    {"42\x99", 42, false},
-    {"\x99" "42\x99", 0, false},
-    {"-2147483648", 0, false},
-    {"2147483647", INT_MAX, true},
-    {"", 0, false},
-    {" 42", 42, false},
-    {"42 ", 42, false},
-    {"\t\n\v\f\r 42", 42, false},
-    {"blah42", 0, false},
-    {"42blah", 42, false},
-    {"blah42blah", 0, false},
-    {"-273.15", 0, false},
-    {"+98.6", 98, false},
-    {"--123", 0, false},
-    {"++123", 0, false},
-    {"-+123", 0, false},
-    {"+-123", 0, false},
-    {"-", 0, false},
-    {"-2147483649", 0, false},
-    {"-99999999999", 0, false},
-    {"4294967295", UINT_MAX, true},
-    {"4294967296", UINT_MAX, false},
-    {"99999999999", UINT_MAX, false},
+      {"0", 0, true},
+      {"42", 42, true},
+      {"42\x99", 42, false},
+      {"\x99"
+       "42\x99",
+       0, false},
+      {"-2147483648", 0, false},
+      {"2147483647", INT_MAX, true},
+      {"", 0, false},
+      {" 42", 42, false},
+      {"42 ", 42, false},
+      {"\t\n\v\f\r 42", 42, false},
+      {"blah42", 0, false},
+      {"42blah", 42, false},
+      {"blah42blah", 0, false},
+      {"-273.15", 0, false},
+      {"+98.6", 98, false},
+      {"--123", 0, false},
+      {"++123", 0, false},
+      {"-+123", 0, false},
+      {"+-123", 0, false},
+      {"-", 0, false},
+      {"-2147483649", 0, false},
+      {"-99999999999", 0, false},
+      {"4294967295", UINT_MAX, true},
+      {"4294967296", UINT_MAX, false},
+      {"99999999999", UINT_MAX, false},
   };
 
   for (const auto& i : cases) {
@@ -227,7 +232,7 @@
   EXPECT_EQ(6U, output);
 
   output = 0;
-  const char16 negative_wide_input[] = { 0xFF4D, '4', '2', 0};
+  const char16 negative_wide_input[] = {0xFF4D, '4', '2', 0};
   EXPECT_FALSE(StringToUint(string16(negative_wide_input), &output));
   EXPECT_EQ(0U, output);
 }
@@ -439,35 +444,35 @@
     int64_t output;
     bool success;
   } cases[] = {
-    {"0", 0, true},
-    {"42", 66, true},
-    {"-42", -66, true},
-    {"+42", 66, true},
-    {"7fffffff", INT_MAX, true},
-    {"-80000000", INT_MIN, true},
-    {"80000000", INT_MAX, false},  // Overflow test.
-    {"-80000001", INT_MIN, false},  // Underflow test.
-    {"0x42", 66, true},
-    {"-0x42", -66, true},
-    {"+0x42", 66, true},
-    {"0x7fffffff", INT_MAX, true},
-    {"-0x80000000", INT_MIN, true},
-    {"-80000000", INT_MIN, true},
-    {"80000000", INT_MAX, false},  // Overflow test.
-    {"-80000001", INT_MIN, false},  // Underflow test.
-    {"0x0f", 15, true},
-    {"0f", 15, true},
-    {" 45", 0x45, false},
-    {"\t\n\v\f\r 0x45", 0x45, false},
-    {" 45", 0x45, false},
-    {"45 ", 0x45, false},
-    {"45:", 0x45, false},
-    {"efgh", 0xef, false},
-    {"0xefgh", 0xef, false},
-    {"hgfe", 0, false},
-    {"-", 0, false},
-    {"", 0, false},
-    {"0x", 0, false},
+      {"0", 0, true},
+      {"42", 66, true},
+      {"-42", -66, true},
+      {"+42", 66, true},
+      {"7fffffff", INT_MAX, true},
+      {"-80000000", INT_MIN, true},
+      {"80000000", INT_MAX, false},   // Overflow test.
+      {"-80000001", INT_MIN, false},  // Underflow test.
+      {"0x42", 66, true},
+      {"-0x42", -66, true},
+      {"+0x42", 66, true},
+      {"0x7fffffff", INT_MAX, true},
+      {"-0x80000000", INT_MIN, true},
+      {"-80000000", INT_MIN, true},
+      {"80000000", INT_MAX, false},   // Overflow test.
+      {"-80000001", INT_MIN, false},  // Underflow test.
+      {"0x0f", 15, true},
+      {"0f", 15, true},
+      {" 45", 0x45, false},
+      {"\t\n\v\f\r 0x45", 0x45, false},
+      {" 45", 0x45, false},
+      {"45 ", 0x45, false},
+      {"45:", 0x45, false},
+      {"efgh", 0xef, false},
+      {"0xefgh", 0xef, false},
+      {"hgfe", 0, false},
+      {"-", 0, false},
+      {"", 0, false},
+      {"0x", 0, false},
   };
 
   for (const auto& i : cases) {
@@ -478,7 +483,9 @@
   // One additional test to verify that conversion of numbers in strings with
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
-  const char input[] = "0xc0ffee\0" "9";
+  const char input[] =
+      "0xc0ffee\0"
+      "9";
   std::string input_string(input, gurl_base::size(input) - 1);
   int output;
   EXPECT_FALSE(HexStringToInt(input_string, &output));
@@ -543,7 +550,9 @@
   // One additional test to verify that conversion of numbers in strings with
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
-  const char input[] = "0xc0ffee\0" "9";
+  const char input[] =
+      "0xc0ffee\0"
+      "9";
   std::string input_string(input, gurl_base::size(input) - 1);
   uint32_t output;
   EXPECT_FALSE(HexStringToUInt(input_string, &output));
@@ -602,7 +611,9 @@
   // One additional test to verify that conversion of numbers in strings with
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
-  const char input[] = "0xc0ffee\0" "9";
+  const char input[] =
+      "0xc0ffee\0"
+      "9";
   std::string input_string(input, gurl_base::size(input) - 1);
   int64_t output;
   EXPECT_FALSE(HexStringToInt64(input_string, &output));
@@ -665,7 +676,9 @@
   // One additional test to verify that conversion of numbers in strings with
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
-  const char input[] = "0xc0ffee\0" "9";
+  const char input[] =
+      "0xc0ffee\0"
+      "9";
   std::string input_string(input, gurl_base::size(input) - 1);
   uint64_t output;
   EXPECT_FALSE(HexStringToUInt64(input_string, &output));
@@ -680,23 +693,23 @@
     size_t output_len;
     bool success;
   } cases[] = {
-    {"0", "", 0, false},  // odd number of characters fails
-    {"00", "\0", 1, true},
-    {"42", "\x42", 1, true},
-    {"-42", "", 0, false},  // any non-hex value fails
-    {"+42", "", 0, false},
-    {"7fffffff", "\x7f\xff\xff\xff", 4, true},
-    {"80000000", "\x80\0\0\0", 4, true},
-    {"deadbeef", "\xde\xad\xbe\xef", 4, true},
-    {"DeadBeef", "\xde\xad\xbe\xef", 4, true},
-    {"0x42", "", 0, false},  // leading 0x fails (x is not hex)
-    {"0f", "\xf", 1, true},
-    {"45  ", "\x45", 1, false},
-    {"efgh", "\xef", 1, false},
-    {"", "", 0, false},
-    {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true},
-    {"0123456789ABCDEF012345",
-     "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", 11, true},
+      {"0", "", 0, false},  // odd number of characters fails
+      {"00", "\0", 1, true},
+      {"42", "\x42", 1, true},
+      {"-42", "", 0, false},  // any non-hex value fails
+      {"+42", "", 0, false},
+      {"7fffffff", "\x7f\xff\xff\xff", 4, true},
+      {"80000000", "\x80\0\0\0", 4, true},
+      {"deadbeef", "\xde\xad\xbe\xef", 4, true},
+      {"DeadBeef", "\xde\xad\xbe\xef", 4, true},
+      {"0x42", "", 0, false},  // leading 0x fails (x is not hex)
+      {"0f", "\xf", 1, true},
+      {"45  ", "\x45", 1, false},
+      {"efgh", "\xef", 1, false},
+      {"", "", 0, false},
+      {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true},
+      {"0123456789ABCDEF012345", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45",
+       11, true},
   };
 
   for (size_t test_i = 0; test_i < gurl_base::size(cases); ++test_i) {
@@ -767,77 +780,79 @@
     double output;
     bool success;
   } cases[] = {
-    // Test different forms of zero.
-    {"0", 0.0, true},
-    {"+0", 0.0, true},
-    {"-0", 0.0, true},
-    {"0.0", 0.0, true},
-    {"000000000000000000000000000000.0", 0.0, true},
-    {"0.000000000000000000000000000", 0.0, true},
+      // Test different forms of zero.
+      {"0", 0.0, true},
+      {"+0", 0.0, true},
+      {"-0", 0.0, true},
+      {"0.0", 0.0, true},
+      {"000000000000000000000000000000.0", 0.0, true},
+      {"0.000000000000000000000000000", 0.0, true},
 
-    // Test the answer.
-    {"42", 42.0, true},
-    {"-42", -42.0, true},
+      // Test the answer.
+      {"42", 42.0, true},
+      {"-42", -42.0, true},
 
-    // Test variances of an ordinary number.
-    {"123.45", 123.45, true},
-    {"-123.45", -123.45, true},
-    {"+123.45", 123.45, true},
+      // Test variances of an ordinary number.
+      {"123.45", 123.45, true},
+      {"-123.45", -123.45, true},
+      {"+123.45", 123.45, true},
 
-    // Test different forms of representation.
-    {"2.99792458e8", 299792458.0, true},
-    {"149597870.691E+3", 149597870691.0, true},
-    {"6.", 6.0, true},
+      // Test different forms of representation.
+      {"2.99792458e8", 299792458.0, true},
+      {"149597870.691E+3", 149597870691.0, true},
+      {"6.", 6.0, true},
 
-    // Test around the largest/smallest value that a double can represent.
-    {"9e307", 9e307, true},
-    {"1.7976e308", 1.7976e308, true},
-    {"1.7977e308", HUGE_VAL, false},
-    {"1.797693134862315807e+308", HUGE_VAL, true},
-    {"1.797693134862315808e+308", HUGE_VAL, false},
-    {"9e308", HUGE_VAL, false},
-    {"9e309", HUGE_VAL, false},
-    {"9e999", HUGE_VAL, false},
-    {"9e1999", HUGE_VAL, false},
-    {"9e19999", HUGE_VAL, false},
-    {"9e99999999999999999999", HUGE_VAL, false},
-    {"-9e307", -9e307, true},
-    {"-1.7976e308", -1.7976e308, true},
-    {"-1.7977e308", -HUGE_VAL, false},
-    {"-1.797693134862315807e+308", -HUGE_VAL, true},
-    {"-1.797693134862315808e+308", -HUGE_VAL, false},
-    {"-9e308", -HUGE_VAL, false},
-    {"-9e309", -HUGE_VAL, false},
-    {"-9e999", -HUGE_VAL, false},
-    {"-9e1999", -HUGE_VAL, false},
-    {"-9e19999", -HUGE_VAL, false},
-    {"-9e99999999999999999999", -HUGE_VAL, false},
+      // Test around the largest/smallest value that a double can represent.
+      {"9e307", 9e307, true},
+      {"1.7976e308", 1.7976e308, true},
+      {"1.7977e308", HUGE_VAL, false},
+      {"1.797693134862315807e+308", HUGE_VAL, true},
+      {"1.797693134862315808e+308", HUGE_VAL, false},
+      {"9e308", HUGE_VAL, false},
+      {"9e309", HUGE_VAL, false},
+      {"9e999", HUGE_VAL, false},
+      {"9e1999", HUGE_VAL, false},
+      {"9e19999", HUGE_VAL, false},
+      {"9e99999999999999999999", HUGE_VAL, false},
+      {"-9e307", -9e307, true},
+      {"-1.7976e308", -1.7976e308, true},
+      {"-1.7977e308", -HUGE_VAL, false},
+      {"-1.797693134862315807e+308", -HUGE_VAL, true},
+      {"-1.797693134862315808e+308", -HUGE_VAL, false},
+      {"-9e308", -HUGE_VAL, false},
+      {"-9e309", -HUGE_VAL, false},
+      {"-9e999", -HUGE_VAL, false},
+      {"-9e1999", -HUGE_VAL, false},
+      {"-9e19999", -HUGE_VAL, false},
+      {"-9e99999999999999999999", -HUGE_VAL, false},
 
-    // Test more exponents.
-    {"1e-2", 0.01, true},
-    {"42 ", 42.0, false},
-    {" 1e-2", 0.01, false},
-    {"1e-2 ", 0.01, false},
-    {"-1E-7", -0.0000001, true},
-    {"01e02", 100, true},
-    {"2.3e15", 2.3e15, true},
-    {"100e-309", 100e-309, true},
+      // Test more exponents.
+      {"1e-2", 0.01, true},
+      {"42 ", 42.0, false},
+      {" 1e-2", 0.01, false},
+      {"1e-2 ", 0.01, false},
+      {"-1E-7", -0.0000001, true},
+      {"01e02", 100, true},
+      {"2.3e15", 2.3e15, true},
+      {"100e-309", 100e-309, true},
 
-    // Test some invalid cases.
-    {"\t\n\v\f\r -123.45e2", -12345.0, false},
-    {"+123 e4", 123.0, false},
-    {"123e ", 123.0, false},
-    {"123e", 123.0, false},
-    {" 2.99", 2.99, false},
-    {"1e3.4", 1000.0, false},
-    {"nothing", 0.0, false},
-    {"-", 0.0, false},
-    {"+", 0.0, false},
-    {"", 0.0, false},
+      // Test some invalid cases.
+      {"\t\n\v\f\r -123.45e2", -12345.0, false},
+      {"+123 e4", 123.0, false},
+      {"123e ", 123.0, false},
+      {"123e", 123.0, false},
+      {"10.5px", 10.5, false},
+      {"11.5e2em", 1150, false},
+      {" 2.99", 2.99, false},
+      {"1e3.4", 1000.0, false},
+      {"nothing", 0.0, false},
+      {"-", 0.0, false},
+      {"+", 0.0, false},
+      {"", 0.0, false},
 
-    // crbug.org/588726
-    {"-0.0010000000000000000000000000000000000000001e-256",
-     -1.0000000000000001e-259, true},
+      // crbug.org/588726
+      {"-0.0010000000000000000000000000000000000000001e-256",
+       -1.0000000000000001e-259, true},
   };
 
   for (size_t i = 0; i < gurl_base::size(cases); ++i) {
@@ -854,7 +869,9 @@
   // One additional test to verify that conversion of numbers in strings with
   // embedded NUL characters.  The NUL and extra data after it should be
   // interpreted as junk after the number.
-  const char input[] = "3.14\0" "159";
+  const char input[] =
+      "3.14\0"
+      "159";
   std::string input_string(input, gurl_base::size(input) - 1);
   double output;
   EXPECT_FALSE(StringToDouble(input_string, &output));
@@ -886,8 +903,8 @@
   double input = 0;
   memcpy(&input, input_bytes, gurl_base::size(input_bytes));
   EXPECT_EQ("1.335179083776e+12", NumberToString(input));
-  const char input_bytes2[8] =
-      {0, 0, 0, '\xa0', '\xda', '\x6c', '\x73', '\x42'};
+  const char input_bytes2[8] = {0,      0,      0,      '\xa0',
+                                '\xda', '\x6c', '\x73', '\x42'};
   input = 0;
   memcpy(&input, input_bytes2, gurl_base::size(input_bytes2));
   EXPECT_EQ("1.33489033216e+12", NumberToString(input));
diff --git a/base/strings/string_piece.cc b/base/strings/string_piece.cc
index ee043de..62ba11f 100644
--- a/base/strings/string_piece.cc
+++ b/base/strings/string_piece.cc
@@ -387,26 +387,5 @@
   return find_last_not_ofT(self, c, pos);
 }
 
-template<typename STR>
-BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self,
-                              size_t pos,
-                              size_t n) {
-  if (pos > self.size()) pos = self.size();
-  if (n > self.size() - pos) n = self.size() - pos;
-  return BasicStringPiece<STR>(self.data() + pos, n);
-}
-
-StringPiece substr(const StringPiece& self,
-                   size_t pos,
-                   size_t n) {
-  return substrT(self, pos, n);
-}
-
-StringPiece16 substr(const StringPiece16& self,
-                     size_t pos,
-                     size_t n) {
-  return substrT(self, pos, n);
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h
index d155496..de54e94 100644
--- a/base/strings/string_piece.h
+++ b/base/strings/string_piece.h
@@ -129,13 +129,6 @@
                                     char c,
                                     size_t pos);
 
-BASE_EXPORT StringPiece substr(const StringPiece& self,
-                               size_t pos,
-                               size_t n);
-BASE_EXPORT StringPiece16 substr(const StringPiece16& self,
-                                 size_t pos,
-                                 size_t n);
-
 }  // namespace internal
 
 // BasicStringPiece ------------------------------------------------------------
@@ -205,19 +198,19 @@
   constexpr const value_type* data() const { return ptr_; }
   constexpr size_type size() const noexcept { return length_; }
   constexpr size_type length() const noexcept { return length_; }
-  bool empty() const { return length_ == 0; }
+  constexpr bool empty() const noexcept { return length_ == 0; }
 
   constexpr value_type operator[](size_type i) const {
     GURL_CHECK(i < length_);
     return ptr_[i];
   }
 
-  value_type front() const {
+  constexpr value_type front() const {
     GURL_CHECK_NE(0UL, length_);
     return ptr_[0];
   }
 
-  value_type back() const {
+  constexpr value_type back() const {
     GURL_CHECK_NE(0UL, length_);
     return ptr_[length_ - 1];
   }
@@ -244,19 +237,24 @@
   }
 
   // This is the style of conversion preferred by std::string_view in C++17.
-  explicit operator STRING_TYPE() const { return as_string(); }
-
-  STRING_TYPE as_string() const {
-    // std::string doesn't like to take a NULL pointer even with a 0 size.
+  explicit operator STRING_TYPE() const {
     return empty() ? STRING_TYPE() : STRING_TYPE(data(), size());
   }
 
-  const_iterator begin() const { return ptr_; }
-  const_iterator end() const { return ptr_ + length_; }
-  const_reverse_iterator rbegin() const {
+  // Deprecated, use operator STRING_TYPE() instead.
+  // TODO(crbug.com/1049498): Remove for all STRING_TYPEs.
+  template <typename StrT = STRING_TYPE,
+            typename = std::enable_if_t<std::is_same<StrT, std::string>::value>>
+  STRING_TYPE as_string() const {
+    return STRING_TYPE(*this);
+  }
+
+  constexpr const_iterator begin() const noexcept { return ptr_; }
+  constexpr const_iterator end() const noexcept { return ptr_ + length_; }
+  constexpr const_reverse_iterator rbegin() const noexcept {
     return const_reverse_iterator(ptr_ + length_);
   }
-  const_reverse_iterator rend() const {
+  constexpr const_reverse_iterator rend() const noexcept {
     return const_reverse_iterator(ptr_);
   }
 
@@ -267,21 +265,6 @@
     return internal::copy(*this, buf, n, pos);
   }
 
-  // Does "this" start with "x"
-  constexpr bool starts_with(BasicStringPiece x) const noexcept {
-    return (
-        (this->length_ >= x.length_) &&
-        (CharTraits<value_type>::compare(this->ptr_, x.ptr_, x.length_) == 0));
-  }
-
-  // Does "this" end with "x"
-  constexpr bool ends_with(BasicStringPiece x) const noexcept {
-    return ((this->length_ >= x.length_) &&
-            (CharTraits<value_type>::compare(
-                 this->ptr_ + (this->length_ - x.length_), x.ptr_, x.length_) ==
-             0));
-  }
-
   // find: Search for a character or substring at a given offset.
   size_type find(const BasicStringPiece<STRING_TYPE>& s,
                  size_type pos = 0) const {
@@ -339,9 +322,11 @@
   }
 
   // substr.
-  BasicStringPiece substr(size_type pos,
-                          size_type n = BasicStringPiece::npos) const {
-    return internal::substr(*this, pos, n);
+  constexpr BasicStringPiece substr(
+      size_type pos,
+      size_type n = BasicStringPiece::npos) const {
+    GURL_CHECK_LE(pos, size());
+    return {data() + pos, std::min(n, size() - pos)};
   }
 
  protected:
diff --git a/base/strings/string_piece_unittest.cc b/base/strings/string_piece_unittest.cc
index 0777549..e0d812b 100644
--- a/base/strings/string_piece_unittest.cc
+++ b/base/strings/string_piece_unittest.cc
@@ -299,8 +299,8 @@
   ASSERT_EQ(a.rfind(c, 0U), Piece::npos);
   ASSERT_EQ(b.rfind(c), Piece::npos);
   ASSERT_EQ(b.rfind(c, 0U), Piece::npos);
-  ASSERT_EQ(a.rfind(d), static_cast<size_t>(a.as_string().rfind(TypeParam())));
-  ASSERT_EQ(a.rfind(e), a.as_string().rfind(TypeParam()));
+  ASSERT_EQ(a.rfind(d), static_cast<size_t>(a.rfind(TypeParam())));
+  ASSERT_EQ(a.rfind(e), a.rfind(TypeParam()));
   ASSERT_EQ(a.rfind(d), static_cast<size_t>(TypeParam(a).rfind(TypeParam())));
   ASSERT_EQ(a.rfind(e), TypeParam(a).rfind(TypeParam()));
   ASSERT_EQ(a.rfind(d, 12), 12U);
@@ -474,11 +474,7 @@
   ASSERT_EQ(a.substr(23, 99), c);
   ASSERT_EQ(a.substr(0), a);
   ASSERT_EQ(a.substr(3, 2), TestFixture::as_string("de"));
-  // empty string nonsense
-  ASSERT_EQ(a.substr(99, 2), e);
-  ASSERT_EQ(d.substr(99), e);
   ASSERT_EQ(d.substr(0, 99), e);
-  ASSERT_EQ(d.substr(99, 99), e);
 }
 
 TYPED_TEST(CommonStringPieceTest, CheckCustom) {
@@ -521,12 +517,6 @@
   c = {foobar.c_str(), 7};  // Note, has an embedded NULL
   ASSERT_NE(c, a);
 
-  // as_string
-  TypeParam s3(a.as_string().c_str(), 7);  // Note, has an embedded NULL
-  ASSERT_EQ(c, s3);
-  TypeParam s4(e.as_string());
-  ASSERT_TRUE(s4.empty());
-
   // operator STRING_TYPE()
   TypeParam s5(TypeParam(a).c_str(), 7);  // Note, has an embedded NULL
   ASSERT_EQ(c, s5);
@@ -543,30 +533,6 @@
   StringPiece e;
   std::string s2;
 
-  // starts_with
-  ASSERT_TRUE(a.starts_with(a));
-  ASSERT_TRUE(a.starts_with("foo"));
-  ASSERT_TRUE(a.starts_with(e));
-  ASSERT_TRUE(b.starts_with(s1));
-  ASSERT_TRUE(b.starts_with(b));
-  ASSERT_TRUE(b.starts_with(e));
-  ASSERT_TRUE(e.starts_with(""));
-  ASSERT_TRUE(!a.starts_with(b));
-  ASSERT_TRUE(!b.starts_with(a));
-  ASSERT_TRUE(!e.starts_with(a));
-
-  // ends with
-  ASSERT_TRUE(a.ends_with(a));
-  ASSERT_TRUE(a.ends_with("bar"));
-  ASSERT_TRUE(a.ends_with(e));
-  ASSERT_TRUE(b.ends_with(s1));
-  ASSERT_TRUE(b.ends_with(b));
-  ASSERT_TRUE(b.ends_with(e));
-  ASSERT_TRUE(e.ends_with(""));
-  ASSERT_TRUE(!a.ends_with(b));
-  ASSERT_TRUE(!b.ends_with(a));
-  ASSERT_TRUE(!e.ends_with(a));
-
   StringPiece c;
   c = {"foobar", 6};
   ASSERT_EQ(c, a);
@@ -584,10 +550,6 @@
   TypeParam str(s);
   ASSERT_EQ(str.length(), 0U);
   ASSERT_EQ(str, TypeParam());
-
-  str = s.as_string();
-  ASSERT_EQ(str.length(), 0U);
-  ASSERT_EQ(str, TypeParam());
 }
 
 TYPED_TEST(CommonStringPieceTest, CheckComparisons2) {
@@ -607,21 +569,6 @@
   ASSERT_GT(abc.compare(BasicStringPiece<TypeParam>(alphabet_y)), 0);
 }
 
-// Test operations only supported by std::string version.
-TEST(StringPieceTest, CheckComparisons2) {
-  StringPiece abc("abcdefghijklmnopqrstuvwxyz");
-
-  // starts_with
-  ASSERT_TRUE(abc.starts_with(abc));
-  ASSERT_TRUE(abc.starts_with("abcdefghijklm"));
-  ASSERT_TRUE(!abc.starts_with("abcdefguvwxyz"));
-
-  // ends_with
-  ASSERT_TRUE(abc.ends_with(abc));
-  ASSERT_TRUE(!abc.ends_with("abcdefguvwxyz"));
-  ASSERT_TRUE(abc.ends_with("nopqrstuvwxyz"));
-}
-
 TYPED_TEST(CommonStringPieceTest, StringCompareNotAmbiguous) {
   ASSERT_TRUE(TestFixture::as_string("hello").c_str() ==
               TestFixture::as_string("hello"));
@@ -656,10 +603,7 @@
 TEST(StringPiece16Test, CheckConversion) {
   // Make sure that we can convert from UTF8 to UTF16 and back. We use a two
   // byte character (G clef) to test this.
-  ASSERT_EQ(
-      UTF16ToUTF8(
-          StringPiece16(UTF8ToUTF16("\xf0\x9d\x84\x9e")).as_string()),
-      "\xf0\x9d\x84\x9e");
+  ASSERT_EQ(UTF16ToUTF8(UTF8ToUTF16("\xf0\x9d\x84\x9e")), "\xf0\x9d\x84\x9e");
 }
 
 TYPED_TEST(CommonStringPieceTest, CheckConstructors) {
@@ -730,6 +674,11 @@
     StringPiece piece;
     ASSERT_DEATH_IF_SUPPORTED(piece.remove_prefix(1), "");
   }
+
+  {
+    StringPiece piece;
+    ASSERT_DEATH_IF_SUPPORTED(piece.substr(1), "");
+  }
 }
 
 TEST(StringPieceTest, ConstexprData) {
@@ -769,6 +718,14 @@
   }
 }
 
+TEST(StringPieceTest, ConstexprFront) {
+  static_assert(StringPiece("abc").front() == 'a', "");
+}
+
+TEST(StringPieceTest, ConstexprBack) {
+  static_assert(StringPiece("abc").back() == 'c', "");
+}
+
 TEST(StringPieceTest, Compare) {
   constexpr StringPiece piece = "def";
 
@@ -783,32 +740,19 @@
   static_assert(piece.compare("ghij") == -1, "");
 }
 
-TEST(StringPieceTest, StartsWith) {
-  constexpr StringPiece piece("abc");
+TEST(StringPieceTest, Substr) {
+  constexpr StringPiece piece = "abcdefghijklmnopqrstuvwxyz";
 
-  static_assert(piece.starts_with(""), "");
-  static_assert(piece.starts_with("a"), "");
-  static_assert(piece.starts_with("ab"), "");
-  static_assert(piece.starts_with("abc"), "");
-
-  static_assert(!piece.starts_with("b"), "");
-  static_assert(!piece.starts_with("bc"), "");
-
-  static_assert(!piece.starts_with("abcd"), "");
-}
-
-TEST(StringPieceTest, EndsWith) {
-  constexpr StringPiece piece("abc");
-
-  static_assert(piece.ends_with(""), "");
-  static_assert(piece.ends_with("c"), "");
-  static_assert(piece.ends_with("bc"), "");
-  static_assert(piece.ends_with("abc"), "");
-
-  static_assert(!piece.ends_with("a"), "");
-  static_assert(!piece.ends_with("ab"), "");
-
-  static_assert(!piece.ends_with("abcd"), "");
+  static_assert(piece.substr(0, 2) == "ab", "");
+  static_assert(piece.substr(0, 3) == "abc", "");
+  static_assert(piece.substr(0, 4) == "abcd", "");
+  static_assert(piece.substr(3, 2) == "de", "");
+  static_assert(piece.substr(3, 3) == "def", "");
+  static_assert(piece.substr(23) == "xyz", "");
+  static_assert(piece.substr(23, 3) == "xyz", "");
+  static_assert(piece.substr(23, 99) == "xyz", "");
+  static_assert(piece.substr(0) == piece, "");
+  static_assert(piece.substr(0, 99) == piece, "");
 }
 
 }  // namespace base
diff --git a/base/strings/string_split.h b/base/strings/string_split.h
index d9676c7..039a049 100644
--- a/base/strings/string_split.h
+++ b/base/strings/string_split.h
@@ -45,8 +45,7 @@
 // To split on either commas or semicolons, keeping all whitespace:
 //
 //   std::vector<std::string> tokens = gurl_base::SplitString(
-//       input, ", WARN_UNUSED_RESULT;", gurl_base::KEEP_WHITESPACE,
-//       gurl_base::SPLIT_WANT_ALL) WARN_UNUSED_RESULT;
+//       input, ",;", gurl_base::KEEP_WHITESPACE, gurl_base::SPLIT_WANT_ALL);
 BASE_EXPORT std::vector<std::string> SplitString(StringPiece input,
                                                  StringPiece separators,
                                                  WhitespaceHandling whitespace,
diff --git a/base/strings/string_util.h b/base/strings/string_util.h
index 1445283..f43a8ac 100644
--- a/base/strings/string_util.h
+++ b/base/strings/string_util.h
@@ -313,18 +313,22 @@
   INSENSITIVE_ASCII,
 };
 
-BASE_EXPORT bool StartsWith(StringPiece str,
-                            StringPiece search_for,
-                            CompareCase case_sensitivity);
-BASE_EXPORT bool StartsWith(StringPiece16 str,
-                            StringPiece16 search_for,
-                            CompareCase case_sensitivity);
-BASE_EXPORT bool EndsWith(StringPiece str,
-                          StringPiece search_for,
-                          CompareCase case_sensitivity);
-BASE_EXPORT bool EndsWith(StringPiece16 str,
-                          StringPiece16 search_for,
-                          CompareCase case_sensitivity);
+BASE_EXPORT bool StartsWith(
+    StringPiece str,
+    StringPiece search_for,
+    CompareCase case_sensitivity = CompareCase::SENSITIVE);
+BASE_EXPORT bool StartsWith(
+    StringPiece16 str,
+    StringPiece16 search_for,
+    CompareCase case_sensitivity = CompareCase::SENSITIVE);
+BASE_EXPORT bool EndsWith(
+    StringPiece str,
+    StringPiece search_for,
+    CompareCase case_sensitivity = CompareCase::SENSITIVE);
+BASE_EXPORT bool EndsWith(
+    StringPiece16 str,
+    StringPiece16 search_for,
+    CompareCase case_sensitivity = CompareCase::SENSITIVE);
 
 // Determines the type of ASCII character, independent of locale (the C
 // library versions will change based on locale).
diff --git a/base/strings/string_util_internal.h b/base/strings/string_util_internal.h
index ace0665..006aeb0 100644
--- a/base/strings/string_util_internal.h
+++ b/base/strings/string_util_internal.h
@@ -5,8 +5,11 @@
 #ifndef BASE_STRINGS_STRING_UTIL_INTERNAL_H_
 #define BASE_STRINGS_STRING_UTIL_INTERNAL_H_
 
+#include <algorithm>
+
 #include "polyfills/base/logging.h"
 #include "polyfills/base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
 #include "base/third_party/icu/icu_utf.h"
 
@@ -130,7 +133,7 @@
   size_t end = (positions & TRIM_TRAILING)
                    ? input.find_last_not_of(trim_chars) + 1
                    : input.size();
-  return input.substr(begin, end - begin);
+  return input.substr(std::min(begin, input.size()), end - begin);
 }
 
 template <typename STR>
@@ -578,8 +581,7 @@
             ReplacementOffset r_offset(index,
                                        static_cast<int>(formatted.size()));
             r_offsets.insert(
-                std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset,
-                                 &CompareParameter),
+                ranges::upper_bound(r_offsets, r_offset, &CompareParameter),
                 r_offset);
           }
           if (index < substitutions)
diff --git a/base/strings/string_util_posix.h b/base/strings/string_util_posix.h
index 457e258..7d5a67b 100644
--- a/base/strings/string_util_posix.h
+++ b/base/strings/string_util_posix.h
@@ -12,6 +12,7 @@
 #include <wchar.h>
 
 #include "polyfills/base/check.h"
+#include "base/strings/string_piece.h"
 
 namespace gurl_base {
 
@@ -32,6 +33,17 @@
   return ::vswprintf(buffer, size, format, arguments);
 }
 
+// These mirror the APIs in string_util_win.h. Since gurl_base::StringPiece is
+// already the native string type on POSIX platforms these APIs are simple
+// no-ops.
+inline StringPiece AsCrossPlatformPiece(StringPiece str) {
+  return str;
+}
+
+inline StringPiece AsNativeStringPiece(StringPiece str) {
+  return str;
+}
+
 }  // namespace base
 
 #endif  // BASE_STRINGS_STRING_UTIL_POSIX_H_
diff --git a/base/strings/string_util_win.h b/base/strings/string_util_win.h
index 2765748..51a6a2b 100644
--- a/base/strings/string_util_win.h
+++ b/base/strings/string_util_win.h
@@ -107,6 +107,18 @@
   return string16(as_u16cstr(str.data()), str.size());
 }
 
+// Compatibility shim for cross-platform code that passes a StringPieceType to a
+// cross platform string utility function. Most of these functions are only
+// implemented for gurl_base::StringPiece and gurl_base::StringPiece16, which is why
+// gurl_base::WStringPieces need to be converted on API boundaries.
+inline StringPiece16 AsCrossPlatformPiece(WStringPiece str) {
+  return AsStringPiece16(str);
+}
+
+inline WStringPiece AsNativeStringPiece(StringPiece16 str) {
+  return AsWStringPiece(str);
+}
+
 // The following section contains overloads of the cross-platform APIs for
 // std::wstring and gurl_base::WStringPiece. These are only enabled if std::wstring
 // and gurl_base::string16 are distinct types, as otherwise this would result in an
@@ -135,7 +147,7 @@
 
 BASE_EXPORT bool TrimString(WStringPiece input,
                             WStringPiece trim_chars,
-                            std::string* output);
+                            std::wstring* output);
 
 BASE_EXPORT WStringPiece TrimString(WStringPiece input,
                                     WStringPiece trim_chars,
@@ -159,13 +171,15 @@
 
 BASE_EXPORT bool EqualsASCII(StringPiece16 str, StringPiece ascii);
 
-BASE_EXPORT bool StartsWith(WStringPiece str,
-                            WStringPiece search_for,
-                            CompareCase case_sensitivity);
+BASE_EXPORT bool StartsWith(
+    WStringPiece str,
+    WStringPiece search_for,
+    CompareCase case_sensitivity = CompareCase::SENSITIVE);
 
-BASE_EXPORT bool EndsWith(WStringPiece str,
-                          WStringPiece search_for,
-                          CompareCase case_sensitivity);
+BASE_EXPORT bool EndsWith(
+    WStringPiece str,
+    WStringPiece search_for,
+    CompareCase case_sensitivity = CompareCase::SENSITIVE);
 
 BASE_EXPORT void ReplaceFirstSubstringAfterOffset(std::wstring* str,
                                                   size_t start_offset,
diff --git a/base/strings/sys_string_conversions.h b/base/strings/sys_string_conversions.h
index a2b4cce..4183d26 100644
--- a/base/strings/sys_string_conversions.h
+++ b/base/strings/sys_string_conversions.h
@@ -18,7 +18,7 @@
 #include "base/strings/string_piece.h"
 #include "build/build_config.h"
 
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
 #include <CoreFoundation/CoreFoundation.h>
 
 #include "base/mac/scoped_cftyperef.h"
@@ -28,7 +28,7 @@
 #else
 class NSString;
 #endif
-#endif  // OS_MACOSX
+#endif  // OS_APPLE
 
 namespace gurl_base {
 
@@ -63,7 +63,7 @@
 
 // Mac-specific ----------------------------------------------------------------
 
-#if defined(OS_MACOSX)
+#if defined(OS_APPLE)
 
 // Converts between STL strings and CFStringRefs/NSStrings.
 
@@ -89,7 +89,7 @@
 BASE_EXPORT std::string SysNSStringToUTF8(NSString* ref) WARN_UNUSED_RESULT;
 BASE_EXPORT string16 SysNSStringToUTF16(NSString* ref) WARN_UNUSED_RESULT;
 
-#endif  // defined(OS_MACOSX)
+#endif  // defined(OS_APPLE)
 
 }  // namespace base
 
diff --git a/base/strings/sys_string_conversions_unittest.cc b/base/strings/sys_string_conversions_unittest.cc
index 0e78d43..2f31dcc 100644
--- a/base/strings/sys_string_conversions_unittest.cc
+++ b/base/strings/sys_string_conversions_unittest.cc
@@ -75,8 +75,8 @@
   EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
 }
 
-#if defined(OS_LINUX)  // Tests depend on setting a specific Linux locale.
-
+// Tests depend on setting a specific Linux locale.
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 TEST(SysStrings, SysWideToNativeMB) {
 #if !defined(SYSTEM_NATIVE_UTF8)
   ScopedLocale locale("en_US.UTF-8");
@@ -191,6 +191,6 @@
     EXPECT_EQ(wide, trip);
   }
 }
-#endif  // OS_LINUX
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
 }  // namespace base
diff --git a/base/template_util.h b/base/template_util.h
index 8c2b185..4b69c7a 100644
--- a/base/template_util.h
+++ b/base/template_util.h
@@ -89,6 +89,15 @@
                    void_t<typename std::iterator_traits<T>::iterator_category>>
     : std::true_type {};
 
+// Helper to express preferences in an overload set. If more than one overload
+// are available for a given set of parameters the overload with the higher
+// priority will be chosen.
+template <size_t I>
+struct priority_tag : priority_tag<I - 1> {};
+
+template <>
+struct priority_tag<0> {};
+
 }  // namespace internal
 
 // is_trivially_copyable is especially hard to get right.
@@ -224,6 +233,59 @@
 template <typename B>
 struct negation : bool_constant<!static_cast<bool>(B::value)> {};
 
+// Implementation of C++17's std::invoke_result_t.
+//
+// This implementation adds references to `Functor` and `Args` to work around
+// some quirks of std::result_of_t. See the #Notes section of [1] for details.
+//
+// References:
+// [1] https://en.cppreference.com/w/cpp/types/result_of
+// [2] https://wg21.link/meta.type.synop#lib:invoke_result_t
+template <typename Functor, typename... Args>
+using invoke_result_t = std::result_of_t<Functor && (Args && ...)>;
+
+// Simplified implementation of C++20's std::iter_value_t.
+// As opposed to std::iter_value_t, this implementation does not restrict
+// the type of `Iter` and does not consider specializations of
+// `indirectly_readable_traits`.
+//
+// Reference: https://wg21.link/readable.traits#2
+template <typename Iter>
+using iter_value_t = typename std::iterator_traits<
+    std::remove_cv_t<std::remove_reference_t<Iter>>>::value_type;
+
+// Simplified implementation of C++20's std::iter_reference_t.
+// As opposed to std::iter_reference_t, this implementation does not restrict
+// the type of `Iter`.
+//
+// Reference: https://wg21.link/iterator.synopsis#:~:text=iter_reference_t
+template <typename Iter>
+using iter_reference_t = decltype(*std::declval<Iter&>());
+
+// Simplified implementation of C++20's std::indirect_result_t. As opposed to
+// std::indirect_result_t, this implementation does not restrict the type of
+// `Func` and `Iters`.
+//
+// Reference: https://wg21.link/iterator.synopsis#:~:text=indirect_result_t
+template <typename Func, typename... Iters>
+using indirect_result_t = invoke_result_t<Func, iter_reference_t<Iters>...>;
+
+// Simplified implementation of C++20's std::projected. As opposed to
+// std::projected, this implementation does not explicitly restrict the type of
+// `Iter` and `Proj`, but rather does so implicitly by requiring
+// `indirect_result_t<Proj, Iter>` is a valid type. This is required for SFINAE
+// friendliness.
+//
+// Reference: https://wg21.link/projected
+template <typename Iter,
+          typename Proj,
+          typename IndirectResultT = indirect_result_t<Proj, Iter>>
+struct projected {
+  using value_type = std::remove_cv_t<std::remove_reference_t<IndirectResultT>>;
+
+  IndirectResultT operator*() const;  // not defined
+};
+
 }  // namespace base
 
 #undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
diff --git a/base/third_party/icu/BUILD b/base/third_party/icu/BUILD
index ca5c9af..f35bdd4 100644
--- a/base/third_party/icu/BUILD
+++ b/base/third_party/icu/BUILD
@@ -5,7 +5,6 @@
 
 cc_library(
     name = "icu",
-    srcs = ["icu_utf.cc"],
     hdrs = ["icu_utf.h"],
     copts = build_config.default_copts,
     visibility = ["//visibility:public"],
diff --git a/base/third_party/icu/README.chromium b/base/third_party/icu/README.chromium
index 297e89a..4f398d9 100644
--- a/base/third_party/icu/README.chromium
+++ b/base/third_party/icu/README.chromium
@@ -6,12 +6,12 @@
 
 This file has the relevant components from ICU copied to handle basic UTF8/16/32
 conversions. Components are copied from umachine.h, utf.h, utf8.h, and utf16.h
-into icu_utf.h, and from utf_impl.cpp into icu_utf.cc.
+into icu_utf.h.
 
-The main change is that U_/U8_/U16_ prefixes have been replaced with
-CBU_/CBU8_/CBU16_ (for "Chrome Base") to avoid confusion with the "real" ICU
-macros should ICU be in use on the system. For the same reason, the functions
-and types have been put in the "base_icu" namespace.
+The main change is that U_/U8_/U16_/UPRV_ prefixes have been replaced with
+CBU_/CBU8_/CBU16_/CBUPRV_ (for "Chrome Base") to avoid confusion with the "real"
+ICU macros should ICU be in use on the system. For the same reason, the
+functions and types have been put in the "base_icu" namespace.
 
 Note that this license file is marked as NOT_SHIPPED, since a more complete
 ICU license is included from //third_party/icu/README.chromium
diff --git a/base/third_party/icu/icu_utf.cc b/base/third_party/icu/icu_utf.cc
deleted file mode 100644
index a3262b0..0000000
--- a/base/third_party/icu/icu_utf.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-******************************************************************************
-*
-*   Copyright (C) 1999-2012, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-******************************************************************************
-*   file name:  utf_impl.cpp
-*   encoding:   UTF-8
-*   tab size:   8 (not used)
-*   indentation:4
-*
-*   created on: 1999sep13
-*   created by: Markus W. Scherer
-*
-*   This file provides implementation functions for macros in the utfXX.h
-*   that would otherwise be too long as macros.
-*/
-
-#include "base/third_party/icu/icu_utf.h"
-
-namespace base_icu {
-
-// source/common/utf_impl.cpp
-
-static const UChar32
-utf8_errorValue[6]={
-    // Same values as UTF8_ERROR_VALUE_1, UTF8_ERROR_VALUE_2, UTF_ERROR_VALUE,
-    // but without relying on the obsolete unicode/utf_old.h.
-    0x15, 0x9f, 0xffff,
-    0x10ffff
-};
-
-static UChar32
-errorValue(int32_t count, int8_t strict) {
-    if(strict>=0) {
-        return utf8_errorValue[count];
-    } else if(strict==-3) {
-        return 0xfffd;
-    } else {
-        return CBU_SENTINEL;
-    }
-}
-
-/*
- * Handle the non-inline part of the U8_NEXT() and U8_NEXT_FFFD() macros
- * and their obsolete sibling UTF8_NEXT_CHAR_SAFE().
- *
- * U8_NEXT() supports NUL-terminated strings indicated via length<0.
- *
- * The "strict" parameter controls the error behavior:
- * <0  "Safe" behavior of U8_NEXT():
- *     -1: All illegal byte sequences yield U_SENTINEL=-1.
- *     -2: Same as -1, except for lenient treatment of surrogate code points as legal.
- *         Some implementations use this for roundtripping of
- *         Unicode 16-bit strings that are not well-formed UTF-16, that is, they
- *         contain unpaired surrogates.
- *     -3: All illegal byte sequences yield U+FFFD.
- *  0  Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., FALSE):
- *     All illegal byte sequences yield a positive code point such that this
- *     result code point would be encoded with the same number of bytes as
- *     the illegal sequence.
- * >0  Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., TRUE):
- *     Same as the obsolete "safe" behavior, but non-characters are also treated
- *     like illegal sequences.
- *
- * Note that a UBool is the same as an int8_t.
- */
-UChar32
-utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict) {
-    // *pi is one after byte c.
-    int32_t i=*pi;
-    // length can be negative for NUL-terminated strings: Read and validate one byte at a time.
-    if(i==length || c>0xf4) {
-        // end of string, or not a lead byte
-    } else if(c>=0xf0) {
-        // Test for 4-byte sequences first because
-        // U8_NEXT() handles shorter valid sequences inline.
-        uint8_t t1=s[i], t2, t3;
-        c&=7;
-        if(CBU8_IS_VALID_LEAD4_AND_T1(c, t1) &&
-                ++i!=length && (t2=s[i]-0x80)<=0x3f &&
-                ++i!=length && (t3=s[i]-0x80)<=0x3f) {
-            ++i;
-            c=(c<<18)|((t1&0x3f)<<12)|(t2<<6)|t3;
-            // strict: forbid non-characters like U+fffe
-            if(strict<=0 || !CBU_IS_UNICODE_NONCHAR(c)) {
-                *pi=i;
-                return c;
-            }
-        }
-    } else if(c>=0xe0) {
-        c&=0xf;
-        if(strict!=-2) {
-            uint8_t t1=s[i], t2;
-            if(CBU8_IS_VALID_LEAD3_AND_T1(c, t1) &&
-                    ++i!=length && (t2=s[i]-0x80)<=0x3f) {
-                ++i;
-                c=(c<<12)|((t1&0x3f)<<6)|t2;
-                // strict: forbid non-characters like U+fffe
-                if(strict<=0 || !CBU_IS_UNICODE_NONCHAR(c)) {
-                    *pi=i;
-                    return c;
-                }
-            }
-        } else {
-            // strict=-2 -> lenient: allow surrogates
-            uint8_t t1=s[i]-0x80, t2;
-            if(t1<=0x3f && (c>0 || t1>=0x20) &&
-                    ++i!=length && (t2=s[i]-0x80)<=0x3f) {
-                *pi=i+1;
-                return (c<<12)|(t1<<6)|t2;
-            }
-        }
-    } else if(c>=0xc2) {
-        uint8_t t1=s[i]-0x80;
-        if(t1<=0x3f) {
-            *pi=i+1;
-            return ((c-0xc0)<<6)|t1;
-        }
-    }  // else 0x80<=c<0xc2 is not a lead byte
-
-    /* error handling */
-    c=errorValue(i-*pi, strict);
-    *pi=i;
-    return c;
-}
-
-}  // namespace base_icu
diff --git a/base/third_party/icu/icu_utf.h b/base/third_party/icu/icu_utf.h
index 2ba8231..16792c4 100644
--- a/base/third_party/icu/icu_utf.h
+++ b/base/third_party/icu/icu_utf.h
@@ -60,6 +60,25 @@
  */
 #define CBU_SENTINEL (-1)
 
+/**
+ * \def UPRV_BLOCK_MACRO_BEGIN
+ * Defined as the "do" keyword by default.
+ * @internal
+ */
+#ifndef CBUPRV_BLOCK_MACRO_BEGIN
+#define CBUPRV_BLOCK_MACRO_BEGIN do
+#endif
+
+/**
+ * \def UPRV_BLOCK_MACRO_END
+ * Defined as "while (FALSE)" by default.
+ * @internal
+ */
+#ifndef CBUPRV_BLOCK_MACRO_END
+#define CBUPRV_BLOCK_MACRO_END while (0)
+#endif
+
+
 // source/common/unicode/utf.h
 
 /**
@@ -147,21 +166,6 @@
 #define CBU8_IS_VALID_LEAD4_AND_T1(lead, t1) (CBU8_LEAD4_T1_BITS[(uint8_t)(t1)>>4]&(1<<((lead)&7)))
 
 /**
- * Function for handling "next code point" with error-checking.
- *
- * This is internal since it is not meant to be called directly by external clie
-nts;
- * however it is U_STABLE (not U_INTERNAL) since it is called by public macros i
-n this
- * file and thus must remain stable, and should not be hidden when other interna
-l
- * functions are hidden (otherwise public macros would fail to compile).
- * @internal
- */
-UChar32
-utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, ::base_icu::UChar32 c, ::base_icu::UBool strict);
-
-/**
  * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)?
  * @param c 8-bit code unit (byte)
  * @return TRUE or FALSE
@@ -230,29 +234,36 @@
  * @see U8_NEXT_UNSAFE
  * @stable ICU 2.4
  */
-#define CBU8_NEXT(s, i, length, c) { \
+#define CBU8_NEXT(s, i, length, c) CBU8_INTERNAL_NEXT_OR_SUB(s, i, length, c, CBU_SENTINEL)
+
+/** @internal */
+#define CBU8_INTERNAL_NEXT_OR_SUB(s, i, length, c, sub) CBUPRV_BLOCK_MACRO_BEGIN { \
     (c)=(uint8_t)(s)[(i)++]; \
     if(!CBU8_IS_SINGLE(c)) { \
-        uint8_t __t1, __t2; \
-        if( /* handle U+0800..U+FFFF inline */ \
-                (0xe0<=(c) && (c)<0xf0) && \
-                (((i)+1)<(length) || (length)<0) && \
-                CBU8_IS_VALID_LEAD3_AND_T1((c), __t1=(s)[i]) && \
-                (__t2=(s)[(i)+1]-0x80)<=0x3f) { \
-            (c)=(((c)&0xf)<<12)|((__t1&0x3f)<<6)|__t2; \
-            (i)+=2; \
-        } else if( /* handle U+0080..U+07FF inline */ \
-                ((c)<0xe0 && (c)>=0xc2) && \
-                ((i)!=(length)) && \
-                (__t1=(s)[i]-0x80)<=0x3f) { \
-            (c)=(((c)&0x1f)<<6)|__t1; \
-            ++(i); \
+        uint8_t __t = 0; \
+        if((i)!=(length) && \
+            /* fetch/validate/assemble all but last trail byte */ \
+            ((c)>=0xe0 ? \
+                ((c)<0xf0 ?  /* U+0800..U+FFFF except surrogates */ \
+                    CBU8_LEAD3_T1_BITS[(c)&=0xf]&(1<<((__t=(s)[i])>>5)) && \
+                    (__t&=0x3f, 1) \
+                :  /* U+10000..U+10FFFF */ \
+                    ((c)-=0xf0)<=4 && \
+                    CBU8_LEAD4_T1_BITS[(__t=(s)[i])>>4]&(1<<(c)) && \
+                    ((c)=((c)<<6)|(__t&0x3f), ++(i)!=(length)) && \
+                    (__t=(s)[i]-0x80)<=0x3f) && \
+                /* valid second-to-last trail byte */ \
+                ((c)=((c)<<6)|__t, ++(i)!=(length)) \
+            :  /* U+0080..U+07FF */ \
+                (c)>=0xc2 && ((c)&=0x1f, 1)) && \
+            /* last trail byte */ \
+            (__t=(s)[i]-0x80)<=0x3f && \
+            ((c)=((c)<<6)|__t, ++(i), 1)) { \
         } else { \
-            /* function call for "complicated" and error cases */ \
-            (c)=::base_icu::utf8_nextCharSafeBody((const uint8_t *)s, &(i), (length), c, -1); \
+            (c)=(sub);  /* ill-formed*/ \
         } \
     } \
-}
+} CBUPRV_BLOCK_MACRO_END
 
 /**
  * Append a code point to a string, overwriting 1 to 4 bytes.
@@ -267,24 +278,25 @@
  * @see U8_APPEND
  * @stable ICU 2.4
  */
-#define CBU8_APPEND_UNSAFE(s, i, c) { \
-    if((uint32_t)(c)<=0x7f) { \
-        (s)[(i)++]=(uint8_t)(c); \
+#define CBU8_APPEND_UNSAFE(s, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+    uint32_t __uc=(c); \
+    if(__uc<=0x7f) { \
+        (s)[(i)++]=(uint8_t)__uc; \
     } else { \
-        if((uint32_t)(c)<=0x7ff) { \
-            (s)[(i)++]=(uint8_t)(((c)>>6)|0xc0); \
+        if(__uc<=0x7ff) { \
+            (s)[(i)++]=(uint8_t)((__uc>>6)|0xc0); \
         } else { \
-            if((uint32_t)(c)<=0xffff) { \
-                (s)[(i)++]=(uint8_t)(((c)>>12)|0xe0); \
+            if(__uc<=0xffff) { \
+                (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \
             } else { \
-                (s)[(i)++]=(uint8_t)(((c)>>18)|0xf0); \
-                (s)[(i)++]=(uint8_t)((((c)>>12)&0x3f)|0x80); \
+                (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \
+                (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \
             } \
-            (s)[(i)++]=(uint8_t)((((c)>>6)&0x3f)|0x80); \
+            (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
         } \
-        (s)[(i)++]=(uint8_t)(((c)&0x3f)|0x80); \
+        (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
     } \
-}
+} CBUPRV_BLOCK_MACRO_END
 
 // source/common/unicode/utf16.h
 
@@ -384,6 +396,45 @@
 #define CBU16_MAX_LENGTH 2
 
 /**
+ * Get a code point from a string at a random-access offset,
+ * without changing the offset.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The offset may point to either the lead or trail surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the adjacent matching surrogate as well.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * If the offset points to a single, unpaired surrogate, then
+ * c is set to that unpaired surrogate.
+ * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<=i<length
+ * @param length string length
+ * @param c output UChar32 variable
+ * @see U16_GET_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_GET(s, start, i, length, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+    (c)=(s)[i]; \
+    if(CBU16_IS_SURROGATE(c)) { \
+        uint16_t __c2; \
+        if(CBU16_IS_SURROGATE_LEAD(c)) { \
+            if((i)+1!=(length) && CBU16_IS_TRAIL(__c2=(s)[(i)+1])) { \
+                (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \
+            } \
+        } else { \
+            if((i)>(start) && CBU16_IS_LEAD(__c2=(s)[(i)-1])) { \
+                (c)=CBU16_GET_SUPPLEMENTARY(__c2, (c)); \
+            } \
+        } \
+    } \
+} CBUPRV_BLOCK_MACRO_END
+
+/**
  * Get a code point from a string at a code point boundary offset,
  * and advance the offset to the next code point boundary.
  * (Post-incrementing forward iteration.)
@@ -404,7 +455,7 @@
  * @see U16_NEXT_UNSAFE
  * @stable ICU 2.4
  */
-#define CBU16_NEXT(s, i, length, c) { \
+#define CBU16_NEXT(s, i, length, c) CBUPRV_BLOCK_MACRO_BEGIN { \
     (c)=(s)[(i)++]; \
     if(CBU16_IS_LEAD(c)) { \
         uint16_t __c2; \
@@ -413,7 +464,7 @@
             (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \
         } \
     } \
-}
+} CBUPRV_BLOCK_MACRO_END
 
 /**
  * Append a code point to a string, overwriting 1 or 2 code units.
@@ -428,14 +479,88 @@
  * @see U16_APPEND
  * @stable ICU 2.4
  */
-#define CBU16_APPEND_UNSAFE(s, i, c) { \
+#define CBU16_APPEND_UNSAFE(s, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \
     if((uint32_t)(c)<=0xffff) { \
         (s)[(i)++]=(uint16_t)(c); \
     } else { \
         (s)[(i)++]=(uint16_t)(((c)>>10)+0xd7c0); \
         (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \
     } \
-}
+} CBUPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary
+ * at the start of a code point.
+ * If the offset points to the trail surrogate of a surrogate pair,
+ * then the offset is decremented.
+ * Otherwise, it is not modified.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<=i
+ * @see U16_SET_CP_START_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_SET_CP_START(s, start, i) CBUPRV_BLOCK_MACRO_BEGIN { \
+    if(CBU16_IS_TRAIL((s)[i]) && (i)>(start) && CBU16_IS_LEAD((s)[(i)-1])) { \
+        --(i); \
+    } \
+} CBUPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the previous one
+ * and get the code point between them.
+ * (Pre-decrementing backward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The input offset may be the same as the string length.
+ * If the offset is behind a trail surrogate unit
+ * for a supplementary code point, then the macro will read
+ * the preceding lead surrogate as well.
+ * If the offset is behind a lead surrogate or behind a single, unpaired
+ * trail surrogate, then c is set to that unpaired surrogate.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<i
+ * @param c output UChar32 variable
+ * @see U16_PREV_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_PREV(s, start, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+    (c)=(s)[--(i)]; \
+    if(CBU16_IS_TRAIL(c)) { \
+        uint16_t __c2; \
+        if((i)>(start) && CBU16_IS_LEAD(__c2=(s)[(i)-1])) { \
+            --(i); \
+            (c)=CBU16_GET_SUPPLEMENTARY(__c2, (c)); \
+        } \
+    } \
+} CBUPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary after a code point.
+ * If the offset is behind the lead surrogate of a surrogate pair,
+ * then the offset is incremented.
+ * Otherwise, it is not modified.
+ * The input offset may be the same as the string length.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const UChar * string
+ * @param start int32_t starting string offset (usually 0)
+ * @param i int32_t string offset, start<=i<=length
+ * @param length int32_t string length
+ * @see U16_SET_CP_LIMIT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_SET_CP_LIMIT(s, start, i, length) CBUPRV_BLOCK_MACRO_BEGIN { \
+    if((start)<(i) && ((i)<(length) || (length)<0) && CBU16_IS_LEAD((s)[(i)-1]) && CBU16_IS_TRAIL((s)[i])) { \
+        ++(i); \
+    } \
+} CBUPRV_BLOCK_MACRO_END
 
 }  // namesapce base_icu
 
diff --git a/build/build_config.h b/build/build_config.h
index 6a6028d..c69df41 100644
--- a/build/build_config.h
+++ b/build/build_config.h
@@ -3,15 +3,35 @@
 // found in the LICENSE file.
 
 // This file adds defines about the platform we're currently building on.
+//
 //  Operating System:
-//    OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) /
-//    OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI
-//    OS_CHROMEOS is set by the build system
+//    OS_AIX / OS_ANDROID / OS_ASMJS / OS_FREEBSD / OS_FUCHSIA / OS_IOS /
+//    OS_LINUX / OS_MAC / OS_NACL (SFI or NONSFI) / OS_NETBSD / OS_OPENBSD /
+//    OS_QNX / OS_SOLARIS / OS_WIN
+//  Operating System family:
+//    OS_APPLE: IOS or MAC
+//    OS_BSD: FREEBSD or NETBSD or OPENBSD
+//    OS_POSIX: AIX or ANDROID or ASMJS or CHROMEOS or FREEBSD or IOS or LINUX
+//              or MAC or NACL or NETBSD or OPENBSD or QNX or SOLARIS
+//
+//  /!\ Note: OS_CHROMEOS is set by the build system, not this file
+//
 //  Compiler:
 //    COMPILER_MSVC / COMPILER_GCC
+//
 //  Processor:
-//    ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64)
-//    ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
+//    ARCH_CPU_ARM64 / ARCH_CPU_ARMEL / ARCH_CPU_MIPS / ARCH_CPU_MIPS64 /
+//    ARCH_CPU_MIPS64EL / ARCH_CPU_MIPSEL / ARCH_CPU_PPC64 / ARCH_CPU_S390 /
+//    ARCH_CPU_S390X / ARCH_CPU_X86 / ARCH_CPU_X86_64
+//  Processor family:
+//    ARCH_CPU_ARM_FAMILY: ARMEL or ARM64
+//    ARCH_CPU_MIPS_FAMILY: MIPS64EL or MIPSEL or MIPS64 or MIPS
+//    ARCH_CPU_PPC64_FAMILY: PPC64
+//    ARCH_CPU_S390_FAMILY: S390 or S390X
+//    ARCH_CPU_X86_FAMILY: X86 or X86_64
+//  Processor features:
+//    ARCH_CPU_31_BITS / ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
+//    ARCH_CPU_BIG_ENDIAN / ARCH_CPU_LITTLE_ENDIAN
 
 #ifndef BUILD_BUILD_CONFIG_H_
 #define BUILD_BUILD_CONFIG_H_
@@ -31,20 +51,25 @@
 #elif defined(ANDROID)
 #define OS_ANDROID 1
 #elif defined(__APPLE__)
-// only include TargetConditions after testing ANDROID as some android builds
-// on mac don't have this header available and it's not needed unless the target
-// is really mac/ios.
+// Only include TargetConditionals after testing ANDROID as some Android builds
+// on the Mac have this header available and it's not needed unless the target
+// is really an Apple platform.
 #include <TargetConditionals.h>
-#define OS_MACOSX 1
 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
 #define OS_IOS 1
+#else
+#define OS_MAC 1
 #endif  // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
 #elif defined(__linux__)
+#if !defined(OS_CHROMEOS)
+// Do not define OS_LINUX on Chrome OS build.
+// The OS_CHROMEOS macro is defined in GN.
 #define OS_LINUX 1
-// include a system header to pull in features.h for glibc/uclibc macros.
+#endif  // !defined(OS_CHROMEOS)
+// Include a system header to pull in features.h for glibc/uclibc macros.
 #include <unistd.h>
 #if defined(__GLIBC__) && !defined(__UCLIBC__)
-// we really are using glibc, not uClibc pretending to be glibc
+// We really are using glibc, not uClibc pretending to be glibc.
 #define LIBC_GLIBC 1
 #endif
 #elif defined(_WIN32)
@@ -71,6 +96,10 @@
 // NOTE: Adding a new port? Please follow
 // https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md
 
+#if defined(OS_MAC) || defined(OS_IOS)
+#define OS_APPLE 1
+#endif
+
 // For access to standard BSD features, use OS_BSD instead of a
 // more specific macro.
 #if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD)
@@ -79,10 +108,11 @@
 
 // For access to standard POSIXish features, use OS_POSIX instead of a
 // more specific macro.
-#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) ||    \
-    defined(OS_FREEBSD) || defined(OS_LINUX) || defined(OS_MACOSX) || \
-    defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) ||  \
-    defined(OS_QNX) || defined(OS_SOLARIS)
+#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) ||  \
+    defined(OS_FREEBSD) || defined(OS_IOS) || defined(OS_LINUX) ||  \
+    defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_NACL) ||  \
+    defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_QNX) || \
+    defined(OS_SOLARIS)
 #define OS_POSIX 1
 #endif
 
diff --git a/copy.bara.sky b/copy.bara.sky
index 328fbab..fefc63d 100644
--- a/copy.bara.sky
+++ b/copy.bara.sky
@@ -14,8 +14,11 @@
         "LICENSE",
         "base/compiler_specific.h",
         "base/containers/checked_iterators.h",
+        "base/containers/contiguous_iterator.h",
         "base/containers/span.h",
         "base/containers/util.h",
+        "base/functional/*.h",
+        "base/ranges/*.h",
         "base/macros.h",
         "base/debug/leak_annotations.h",
         "base/no_destructor.h",
@@ -88,7 +91,7 @@
     core.replace(
         '"third_party/icu/source/common/unicode/${file}.h"',
         "<unicode/${file}.h>",
-        regex_groups = {"file": "\w+"},
+        regex_groups = {"file": "\\w+"},
     ),
 ]
 
diff --git a/url/gurl.cc b/url/gurl.cc
index 5ac3ea1..68f3f8c 100644
--- a/url/gurl.cc
+++ b/url/gurl.cc
@@ -485,7 +485,7 @@
   if (has_host() || has_username() || has_password() || has_port())
     return false;
 
-  if (!path_piece().starts_with(allowed_path))
+  if (!gurl_base::StartsWith(path_piece(), allowed_path))
     return false;
 
   if (path_piece().size() == allowed_path.size()) {
diff --git a/url/gurl_fuzzer.cc b/url/gurl_fuzzer.cc
index a07c195..3b28aea 100644
--- a/url/gurl_fuzzer.cc
+++ b/url/gurl_fuzzer.cc
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include "base/at_exit.h"
+#include "polyfills/base/check_op.h"
 #include "base/i18n/icu_util.h"
+#include "base/no_destructor.h"
 #include "url/gurl.h"
 
 struct TestCase {
@@ -15,53 +17,73 @@
 
 TestCase* test_case = new TestCase();
 
-// Empty replacements cause no change when applied.
-GURL::Replacements* no_op = new GURL::Replacements();
+// Checks that GURL's canonicalization is idempotent. This can help discover
+// issues like https://crbug.com/1128999.
+void CheckIdempotency(const GURL& url) {
+  if (!url.is_valid())
+    return;
+  const std::string& spec = url.spec();
+  GURL recanonicalized(spec);
+  GURL_CHECK(recanonicalized.is_valid());
+  GURL_CHECK_EQ(spec, recanonicalized.spec());
+}
+
+// Checks that |url.spec()| is preserved across a call to ReplaceComponents with
+// zero replacements, which is effectively a copy. This can help discover issues
+// like https://crbug.com/1075515.
+void CheckReplaceComponentsPreservesSpec(const GURL& url) {
+  static const gurl_base::NoDestructor<GURL::Replacements> no_op;
+  GURL copy = url.ReplaceComponents(*no_op);
+  GURL_CHECK_EQ(url.is_valid(), copy.is_valid());
+  if (url.is_valid()) {
+    GURL_CHECK_EQ(url.spec(), copy.spec());
+  }
+}
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   if (size < 1)
     return 0;
-
-  gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data),
-                                       size);
-  GURL url_from_string_piece(string_piece_input);
-  // Copying by applying empty replacements exercises interesting code paths.
-  // This can help discover issues like https://crbug.com/1075515.
-  GURL copy = url_from_string_piece.ReplaceComponents(*no_op);
-  GURL_CHECK_EQ(url_from_string_piece.is_valid(), copy.is_valid());
-  if (url_from_string_piece.is_valid()) {
-    GURL_CHECK_EQ(url_from_string_piece.spec(), copy.spec());
+  {
+    gurl_base::StringPiece string_piece_input(reinterpret_cast<const char*>(data),
+                                         size);
+    const GURL url_from_string_piece(string_piece_input);
+    CheckIdempotency(url_from_string_piece);
+    CheckReplaceComponentsPreservesSpec(url_from_string_piece);
   }
-
   // Test for StringPiece16 if size is even.
   if (size % 2 == 0) {
     gurl_base::StringPiece16 string_piece_input16(
         reinterpret_cast<const gurl_base::char16*>(data), size / 2);
-
-    GURL url_from_string_piece16(string_piece_input16);
+    const GURL url_from_string_piece16(string_piece_input16);
+    CheckIdempotency(url_from_string_piece16);
+    CheckReplaceComponentsPreservesSpec(url_from_string_piece16);
   }
-
   // Resolve relative url tests.
-  size_t size_t_bytes = sizeof(size_t);
-  if (size < size_t_bytes + 1) {
-    return 0;
-  }
-  size_t relative_size =
-      *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes);
-  std::string relative_string(
-      reinterpret_cast<const char*>(data + size_t_bytes), relative_size);
-  gurl_base::StringPiece string_piece_part_input(
-      reinterpret_cast<const char*>(data + size_t_bytes + relative_size),
-      size - relative_size - size_t_bytes);
-  GURL url_from_string_piece_part(string_piece_part_input);
-  url_from_string_piece_part.Resolve(relative_string);
+  {
+    size_t size_t_bytes = sizeof(size_t);
+    if (size < size_t_bytes + 1) {
+      return 0;
+    }
+    size_t relative_size =
+        *reinterpret_cast<const size_t*>(data) % (size - size_t_bytes);
+    std::string relative_string(
+        reinterpret_cast<const char*>(data + size_t_bytes), relative_size);
+    gurl_base::StringPiece string_piece_part_input(
+        reinterpret_cast<const char*>(data + size_t_bytes + relative_size),
+        size - relative_size - size_t_bytes);
+    const GURL url_from_string_piece_part(string_piece_part_input);
+    CheckIdempotency(url_from_string_piece_part);
+    CheckReplaceComponentsPreservesSpec(url_from_string_piece_part);
 
-  if (relative_size % 2 == 0) {
-    gurl_base::string16 relative_string16(
-        reinterpret_cast<const gurl_base::char16*>(data + size_t_bytes),
-        relative_size / 2);
-    url_from_string_piece_part.Resolve(relative_string16);
+    url_from_string_piece_part.Resolve(relative_string);
+
+    if (relative_size % 2 == 0) {
+      gurl_base::string16 relative_string16(
+          reinterpret_cast<const gurl_base::char16*>(data + size_t_bytes),
+          relative_size / 2);
+      url_from_string_piece_part.Resolve(relative_string16);
+    }
   }
   return 0;
 }
diff --git a/url/origin.cc b/url/origin.cc
index 574c512..d04e557 100644
--- a/url/origin.cc
+++ b/url/origin.cc
@@ -244,7 +244,7 @@
   return Origin(Nonce(), tuple_);
 }
 
-std::string Origin::GetDebugString() const {
+std::string Origin::GetDebugString(bool include_nonce) const {
   // Handle non-opaque origins first, as they are simpler.
   if (!opaque()) {
     std::string out = Serialize();
@@ -256,11 +256,15 @@
   // For opaque origins, log the nonce and precursor as well. Without this,
   // EXPECT_EQ failures between opaque origins are nearly impossible to
   // understand.
-  std::string nonce = nonce_->raw_token().is_empty()
-                          ? std::string("nonce TBD")
-                          : nonce_->raw_token().ToString();
-
-  std::string out = gurl_base::StrCat({Serialize(), " [internally: (", nonce, ")"});
+  std::string out = gurl_base::StrCat({Serialize(), " [internally:"});
+  if (include_nonce) {
+    out += " (";
+    if (nonce_->raw_token().is_empty())
+      out += "nonce TBD";
+    else
+      out += nonce_->raw_token().ToString();
+    out += ")";
+  }
   if (!tuple_.IsValid())
     gurl_base::StrAppend(&out, {" anonymous]"});
   else
@@ -283,11 +287,20 @@
   GURL_DCHECK_EQ(0U, port());
 }
 
+gurl_base::Optional<std::string> Origin::SerializeWithNonce() const {
+  return SerializeWithNonceImpl();
+}
+
+gurl_base::Optional<std::string> Origin::SerializeWithNonceAndInitIfNeeded() {
+  GetNonceForSerialization();
+  return SerializeWithNonceImpl();
+}
+
 // The pickle is saved in the following format, in order:
 // string - tuple_.GetURL().spec().
 // uint64_t (if opaque) - high bits of nonce if opaque. 0 if not initialized.
 // uint64_t (if opaque) - low bits of nonce if opaque. 0 if not initialized.
-gurl_base::Optional<std::string> Origin::SerializeWithNonce() const {
+gurl_base::Optional<std::string> Origin::SerializeWithNonceImpl() const {
   if (!opaque() && !tuple_.IsValid())
     return gurl_base::nullopt;
 
@@ -438,7 +451,8 @@
     const url::Origin* value)
     : gurl_base::debug::ScopedCrashKeyString(
           crash_key,
-          value ? value->GetDebugString() : "nullptr") {}
+          value ? value->GetDebugString(false /* include_nonce */)
+                : "nullptr") {}
 
 ScopedOriginCrashKey::~ScopedOriginCrashKey() = default;
 
diff --git a/url/origin.h b/url/origin.h
index 2aef330..f4c1bf8 100644
--- a/url/origin.h
+++ b/url/origin.h
@@ -58,6 +58,7 @@
 namespace net {
 class NetworkIsolationKey;
 class OpaqueNonTransientNetworkIsolationKeyTest;
+class SchemefulSite;
 }  // namespace net
 
 namespace url {
@@ -288,7 +289,7 @@
   // Creates a string representation of the object that can be used for logging
   // and debugging. It serializes the internal state, such as the nonce value
   // and precursor information.
-  std::string GetDebugString() const;
+  std::string GetDebugString(bool include_nonce = true) const;
 
 #if defined(OS_ANDROID)
   gurl_base::android::ScopedJavaLocalRef<jobject> CreateJavaObject() const;
@@ -299,6 +300,9 @@
  private:
   friend class blink::SecurityOrigin;
   friend class net::NetworkIsolationKey;
+  // SchemefulSite needs access to the serialization/deserialization logic which
+  // includes the nonce.
+  friend class net::SchemefulSite;
   friend class net::OpaqueNonTransientNetworkIsolationKeyTest;
   friend class OriginTest;
   friend struct mojo::UrlOriginAdapter;
@@ -395,11 +399,17 @@
   gurl_base::Optional<gurl_base::UnguessableToken> GetNonceForSerialization() const;
 
   // Serializes this Origin, including its nonce if it is opaque. If an opaque
-  // origin's |tuple_| is invalid or the nonce isn't initialized, nullopt is
-  // returned. Use of this method should be limited as an opaque origin will
-  // never be matchable in future browser sessions.
+  // origin's |tuple_| is invalid nullopt is returned. If the nonce is not
+  // initialized, a nonce of 0 is used. Use of this method should be limited as
+  // an opaque origin will never be matchable in future browser sessions.
   gurl_base::Optional<std::string> SerializeWithNonce() const;
 
+  // Like SerializeWithNonce(), but forces |nonce_| to be initialized prior to
+  // serializing.
+  gurl_base::Optional<std::string> SerializeWithNonceAndInitIfNeeded();
+
+  gurl_base::Optional<std::string> SerializeWithNonceImpl() const;
+
   // Deserializes an origin from |ToValueWithNonce|. Returns nullopt if the
   // value was invalid in any way.
   static gurl_base::Optional<Origin> Deserialize(const std::string& value);
diff --git a/url/origin_unittest.cc b/url/origin_unittest.cc
index 8fe1cef..7793b9a 100644
--- a/url/origin_unittest.cc
+++ b/url/origin_unittest.cc
@@ -109,6 +109,11 @@
     return origin.SerializeWithNonce();
   }
 
+  gurl_base::Optional<std::string> SerializeWithNonceAndInitIfNeeded(
+      Origin& origin) {
+    return origin.SerializeWithNonceAndInitIfNeeded();
+  }
+
   gurl_base::Optional<Origin> Deserialize(const std::string& value) {
     return Origin::Deserialize(value);
   }
@@ -846,6 +851,10 @@
       http_opaque_origin.GetDebugString().c_str(),
       ::testing::MatchesRegex(
           "null \\[internally: \\(\\w*\\) derived from http://192.168.9.1\\]"));
+  EXPECT_THAT(
+      http_opaque_origin.GetDebugString(false /* include_nonce */).c_str(),
+      ::testing::MatchesRegex(
+          "null \\[internally: derived from http://192.168.9.1\\]"));
 
   Origin data_origin = Origin::Create(GURL("data:"));
   EXPECT_STREQ(data_origin.GetDebugString().c_str(),
@@ -857,6 +866,9 @@
   EXPECT_THAT(
       data_derived_origin.GetDebugString().c_str(),
       ::testing::MatchesRegex("null \\[internally: \\(\\w*\\) anonymous\\]"));
+  EXPECT_THAT(
+      data_derived_origin.GetDebugString(false /* include_nonce */).c_str(),
+      ::testing::MatchesRegex("null \\[internally: anonymous\\]"));
 
   Origin file_origin = Origin::Create(GURL("file:///etc/passwd"));
   EXPECT_STREQ(file_origin.GetDebugString().c_str(),
@@ -926,6 +938,19 @@
   // Can't use DoEqualityComparisons here since empty nonces are never == unless
   // they are the same object.
   EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString());
+
+  // Now force initialization of the nonce prior to serialization.
+  for (const GURL& url : invalid_urls) {
+    SCOPED_TRACE(url.spec());
+    Origin origin = Origin::Create(url);
+    gurl_base::Optional<std::string> serialized =
+        SerializeWithNonceAndInitIfNeeded(origin);
+    gurl_base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
+    ASSERT_TRUE(deserialized.has_value());
+
+    // The nonce should have been initialized prior to Serialization().
+    EXPECT_EQ(origin, deserialized.value());
+  }
 }
 
 TEST_F(OriginTest, DeserializeValidNonce) {
diff --git a/url/third_party/mozilla/DIR_METADATA b/url/third_party/mozilla/DIR_METADATA
new file mode 100644
index 0000000..fb07a25
--- /dev/null
+++ b/url/third_party/mozilla/DIR_METADATA
@@ -0,0 +1,11 @@
+# Metadata information for this directory.
+#
+# For more information on DIR_METADATA files, see:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
+#
+# For the schema of this file, see Metadata message:
+#   https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
+
+monorail {
+  component: "Internals>Core"
+}
\ No newline at end of file
diff --git a/url/third_party/mozilla/OWNERS b/url/third_party/mozilla/OWNERS
deleted file mode 100644
index 3605f48..0000000
--- a/url/third_party/mozilla/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# COMPONENT: Internals>Core
diff --git a/url/url_canon_etc.cc b/url/url_canon_etc.cc
index 23d1235..8482c35 100644
--- a/url/url_canon_etc.cc
+++ b/url/url_canon_etc.cc
@@ -245,10 +245,9 @@
 }
 
 // clang-format off
-//   Percent-escape all "C0 controls" (0x00-0x1F)
-//   https://infra.spec.whatwg.org/#c0-control along with the characters ' '
-//   (0x20), '"' (0x22), '<' (0x3C), '>' (0x3E), and '`' (0x60):
-const bool kShouldEscapeCharInRef[0x80] = {
+//   Percent-escape all characters from the fragment percent-encode set
+//   https://url.spec.whatwg.org/#fragment-percent-encode-set
+const bool kShouldEscapeCharInFragment[0x80] = {
 //  Control characters (0x00-0x1F)
     true,  true,  true,  true,  true,  true,  true,  true,
     true,  true,  true,  true,  true,  true,  true,  true,
@@ -276,8 +275,8 @@
     false, false, false, false, false, false, false, false,
 //  p      q      r      s      t      u      v      w
     false, false, false, false, false, false, false, false,
-//  x      y      z      {      |      }      ~
-    false, false, false, false, false, false, false
+//  x      y      z      {      |      }      ~      DELETE
+    false, false, false, false, false, false, false, true
 };
 // clang-format on
 
@@ -307,7 +306,7 @@
 
     UCHAR current_char = static_cast<UCHAR>(spec[i]);
     if (current_char < 0x80) {
-      if (kShouldEscapeCharInRef[current_char])
+      if (kShouldEscapeCharInFragment[current_char])
         AppendEscapedChar(static_cast<unsigned char>(spec[i]), output);
       else
         output->push_back(static_cast<char>(spec[i]));
diff --git a/url/url_canon_pathurl.cc b/url/url_canon_pathurl.cc
index 62fe22f..0330b06 100644
--- a/url/url_canon_pathurl.cc
+++ b/url/url_canon_pathurl.cc
@@ -26,13 +26,16 @@
     if (separator)
       output->push_back(separator);
     // Copy the path using path URL's more lax escaping rules (think for
-    // javascript:). We convert to UTF-8 and escape non-ASCII, but leave all
-    // ASCII characters alone. This helps readability of JavaStript.
+    // javascript:). We convert to UTF-8 and escape characters from the
+    // C0 control percent-encode set, but leave all other characters alone.
+    // This helps readability of JavaScript.
+    // https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
+    // https://url.spec.whatwg.org/#c0-control-percent-encode-set
     new_component->begin = output->length();
     int end = component.end();
     for (int i = component.begin; i < end; i++) {
       UCHAR uch = static_cast<UCHAR>(source[i]);
-      if (uch < 0x20 || uch >= 0x80)
+      if (uch < 0x20 || uch > 0x7E)
         AppendUTF8EscapedChar(source, &i, end, output);
       else
         output->push_back(static_cast<char>(uch));
diff --git a/url/url_canon_stdstring.h b/url/url_canon_stdstring.h
index 82ee9db..9b7943a 100644
--- a/url/url_canon_stdstring.h
+++ b/url/url_canon_stdstring.h
@@ -54,33 +54,55 @@
 //
 // The contents of the StringPieces are not copied and must remain valid until
 // the StringPieceReplacements object goes out of scope.
-template<typename STR>
+//
+// In order to make it harder to misuse the API the setters do not accept rvalue
+// references to std::strings.
+// Note: Extra const char* overloads are necessary to break ambiguities that
+// would otherwise exist for char literals.
+template <typename STR>
 class StringPieceReplacements : public Replacements<typename STR::value_type> {
+ private:
+  using CharT = typename STR::value_type;
+  using StringPieceT = gurl_base::BasicStringPiece<STR>;
+  using ParentT = Replacements<CharT>;
+  using SetterFun = void (ParentT::*)(const CharT*, const Component&);
+
+  void SetImpl(SetterFun fun, StringPieceT str) {
+    (this->*fun)(str.data(), Component(0, static_cast<int>(str.size())));
+  }
+
  public:
-  void SetSchemeStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetScheme(s.data(), Component(0, static_cast<int>(s.length())));
-  }
-  void SetUsernameStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetUsername(s.data(), Component(0, static_cast<int>(s.length())));
-  }
-  void SetPasswordStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetPassword(s.data(), Component(0, static_cast<int>(s.length())));
-  }
-  void SetHostStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetHost(s.data(), Component(0, static_cast<int>(s.length())));
-  }
-  void SetPortStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetPort(s.data(), Component(0, static_cast<int>(s.length())));
-  }
-  void SetPathStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetPath(s.data(), Component(0, static_cast<int>(s.length())));
-  }
-  void SetQueryStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetQuery(s.data(), Component(0, static_cast<int>(s.length())));
-  }
-  void SetRefStr(const gurl_base::BasicStringPiece<STR>& s) {
-    this->SetRef(s.data(), Component(0, static_cast<int>(s.length())));
-  }
+  void SetSchemeStr(const CharT* str) { SetImpl(&ParentT::SetScheme, str); }
+  void SetSchemeStr(StringPieceT str) { SetImpl(&ParentT::SetScheme, str); }
+  void SetSchemeStr(const STR&&) = delete;
+
+  void SetUsernameStr(const CharT* str) { SetImpl(&ParentT::SetUsername, str); }
+  void SetUsernameStr(StringPieceT str) { SetImpl(&ParentT::SetUsername, str); }
+  void SetUsernameStr(const STR&&) = delete;
+
+  void SetPasswordStr(const CharT* str) { SetImpl(&ParentT::SetPassword, str); }
+  void SetPasswordStr(StringPieceT str) { SetImpl(&ParentT::SetPassword, str); }
+  void SetPasswordStr(const STR&&) = delete;
+
+  void SetHostStr(const CharT* str) { SetImpl(&ParentT::SetHost, str); }
+  void SetHostStr(StringPieceT str) { SetImpl(&ParentT::SetHost, str); }
+  void SetHostStr(const STR&&) = delete;
+
+  void SetPortStr(const CharT* str) { SetImpl(&ParentT::SetPort, str); }
+  void SetPortStr(StringPieceT str) { SetImpl(&ParentT::SetPort, str); }
+  void SetPortStr(const STR&&) = delete;
+
+  void SetPathStr(const CharT* str) { SetImpl(&ParentT::SetPath, str); }
+  void SetPathStr(StringPieceT str) { SetImpl(&ParentT::SetPath, str); }
+  void SetPathStr(const STR&&) = delete;
+
+  void SetQueryStr(const CharT* str) { SetImpl(&ParentT::SetQuery, str); }
+  void SetQueryStr(StringPieceT str) { SetImpl(&ParentT::SetQuery, str); }
+  void SetQueryStr(const STR&&) = delete;
+
+  void SetRefStr(const CharT* str) { SetImpl(&ParentT::SetRef, str); }
+  void SetRefStr(StringPieceT str) { SetImpl(&ParentT::SetRef, str); }
+  void SetRefStr(const STR&&) = delete;
 };
 
 }  // namespace url